From c2963545b342e60a86db02e627a07b10f9ebb3ed Mon Sep 17 00:00:00 2001 From: markilue <745518019@qq.com> Date: Wed, 14 Dec 2022 12:24:50 +0800 Subject: [PATCH] =?UTF-8?q?leecode=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../leecode/dynamic/T24_MaxProfit.java | 16 +- .../leecode/dynamic/T25_MaxProfit.java | 157 ++++++++++++++++++ .../leecode/dynamic/T26_MaxProfit.java | 102 ++++++++++++ 3 files changed, 267 insertions(+), 8 deletions(-) create mode 100644 Leecode/src/main/java/com/markilue/leecode/dynamic/T25_MaxProfit.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/dynamic/T26_MaxProfit.java diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/T24_MaxProfit.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/T24_MaxProfit.java index 01191cf..ffb9afd 100644 --- a/Leecode/src/main/java/com/markilue/leecode/dynamic/T24_MaxProfit.java +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/T24_MaxProfit.java @@ -17,10 +17,10 @@ import org.junit.Test; public class T24_MaxProfit { @Test - public void test(){ + public void test() { int k = 2; int[] prices = {3, 2, 6, 5, 0, 3}; - System.out.println(maxProfit1(k,prices)); + System.out.println(maxProfit1(k, prices)); } /** @@ -47,11 +47,11 @@ public class T24_MaxProfit { for (int i = 1; i < prices.length; i++) { for (int j = 0; j < k; j++) { - dp[i][2*j+1]=Math.max(dp[i-1][2*j+1],dp[i-1][2*j]-prices[i]); - dp[i][2*(j+1)]=Math.max(dp[i-1][2*(j+1)],dp[i-1][2*j+1]+prices[i]); + dp[i][2 * j + 1] = Math.max(dp[i - 1][2 * j + 1], dp[i - 1][2 * j] - prices[i]); + dp[i][2 * (j + 1)] = Math.max(dp[i - 1][2 * (j + 1)], dp[i - 1][2 * j + 1] + prices[i]); } } - return dp[prices.length-1][2*k]; + return dp[prices.length - 1][2 * k]; } @@ -72,11 +72,11 @@ public class T24_MaxProfit { for (int i = 1; i < prices.length; i++) { for (int j = 0; j < k; j++) { - dp[2*j+1]=Math.max(dp[2*j+1],dp[2*j]-prices[i]); - dp[2*(j+1)]=Math.max(dp[2*(j+1)],dp[2*j+1]+prices[i]); + dp[2 * j + 1] = Math.max(dp[2 * j + 1], dp[2 * j] - prices[i]); + dp[2 * (j + 1)] = Math.max(dp[2 * (j + 1)], dp[2 * j + 1] + prices[i]); } } - return dp[2*k]; + return dp[2 * k]; } } diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/T25_MaxProfit.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/T25_MaxProfit.java new file mode 100644 index 0000000..7cf334e --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/T25_MaxProfit.java @@ -0,0 +1,157 @@ +package com.markilue.leecode.dynamic; + +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.dynamic + *@Author: dingjiawen + *@CreateTime: 2022-12-14 09:45 + *@Description: + * TODO 力扣309题 最佳买卖股票时机含冷冻期: + * 给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。 + * 设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): + * 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。 + * 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 + *@Version: 1.0 + */ +public class T25_MaxProfit { + + @Test + public void test(){ + int[] prices = {1, 2, 3, 0, 2}; + System.out.println(maxProfit(prices)); + } + + /** + * 思路:正如题目所说:对应的交易状态有: [买入, 卖出, 冷冻期],由此可以列举状态转移方程 + * TODO 动归五部曲: + * (1)dp定义:dp[i][0]表示第i天处于买入了的状态最多的利润;dp[i][1]表示第i天处于卖出的状态最多的利润 + * dp[i][2]表示第i天处于冷冻期的状态最多的利润;dp[i][3]表示第i天非冷冻期的状态最多的利润 + * (2)dp状态转移方程: + * 1.dp[i][0]=昨天买入了今天没变化;昨天处于冷冻期今天买入了;除了第一次以外,下次再买一定是冷冻期之后的买 + * dp[i][0]=max(dp[i-1][0],dp[i-1][2]-prices[i],-prices[i]) + * 2.dp[i][1]=昨天处于冷冻期今天没变化;昨天买今天卖入了; + * dp[i][1]=max(dp[i-1][2],dp[i-1][0]+prices[i],dp[i-1][1]) + * 2.dp[i][2]=昨天卖了今天处于冷冻期;昨天处于冷冻期今天没变化 + * dp[i][2]=max(dp[i-1][1],dp[i-1][2]) + * (3)dp初始化:dp[0][0]=-prices[0];dp[0][1]=0;dp[0][2]=0 + * (4)dp遍历顺序:从前往后 + * (5)dp举例推导: 以prices = [1,2,3,0,2]为例 + * [0 1 2] + * i=0 -1 0 0 + * i=1 -1 1 0 + * i=2 -1 2 1 + * i=3 1 2 2 + * i=4 1 3 2 + * 速度击败77.78%,内存击败46.23% 1ms + * @param prices + * @return + */ + public int maxProfit(int[] prices) { + + int[][] dp = new int[prices.length][3]; + dp[0][0]=-prices[0];dp[0][1]=0;dp[0][2]=0; + + for (int i = 1; i < prices.length; i++) { + dp[i][0]=Math.max(Math.max(dp[i-1][0],dp[i-1][2]-prices[i]),-prices[i]); + dp[i][1]=Math.max(Math.max(dp[i-1][2],dp[i-1][0]+prices[i]),dp[i-1][1]); + dp[i][2]=Math.max(dp[i-1][1],dp[i-1][2]); + } + return dp[prices.length-1][1]; + + } + + + /** + * 代码随想录动态规划法:具体状态转移推导,见笔记 + * 把交易的状态分为了四种: + * 1.状态一:买入股票状态(今天买入股票,或者是之前就买入了股票然后没有操作) + * 2.卖出股票状态,这里就有两种卖出股票状态 + * 状态二:两天前就卖出了股票,度过了冷冻期,一直没操作,今天保持卖出股票状态 + * 状态三:今天卖出了股票 + * 3.状态四:今天为冷冻期状态,但冷冻期状态不可持续,只有一天 + * 速度击败77.78%,内存击败27.95% + * @param prices + * @return + */ + public int maxProfit1(int[] prices) { + int n=prices.length; + if (prices == null || n < 2) { + return 0; + } + int[][] dp = new int[n][4]; + + // bad case + dp[0][0] = -prices[0]; + dp[0][1] = 0; + dp[1][0] = 0; + dp[1][1] = 0; + + for (int i = 1; i < prices.length; i++) { + // dp公式 + dp[i][0] = Math.max(dp[i - 1][0], Math.max(dp[i - 1][3], dp[i - 1][1]) - prices[i]); + dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][3]); + dp[i][2] = dp[i - 1][0] + prices[i]; + dp[i][3] = dp[i - 1][2]; + } + + return Math.max(dp[n - 1][3],Math.max(dp[n - 1][1], dp[n - 1][2])); + } + + + /** + * 官方三状态法: + * 速度击败77.78%,内存击败52.16% + * @param prices + * @return + */ + public int maxProfit2(int[] prices) { + if (prices.length == 0) { + return 0; + } + + int n = prices.length; + // f[i][0]: 手上持有股票的最大收益 + // f[i][1]: 手上不持有股票,并且处于冷冻期中的累计最大收益;这里的「处于冷冻期」指的是在第 i 天结束之后的状态 + // f[i][2]: 手上不持有股票,并且不在冷冻期中的累计最大收益;由于不能在冷冻期,所以今天不能卖股票 + int[][] f = new int[n][3]; + f[0][0] = -prices[0]; + for (int i = 1; i < n; ++i) { + f[i][0] = Math.max(f[i - 1][0], f[i - 1][2] - prices[i]);//昨天就持有了,或者没有没持有今天买; + f[i][1] = f[i - 1][0] + prices[i]; //必须是今天刚卖; + f[i][2] = Math.max(f[i - 1][1], f[i - 1][2]); //昨天处于冷冻期;昨天过了冷冻期但是没操作; + } + return Math.max(f[n - 1][1], f[n - 1][2]); + } + + + /** + * 官方滚动数组优化: + * 速度击败100%,内存击败79.39% + * @param prices + * @return + */ + public int maxProfit3(int[] prices) { + if (prices.length == 0) { + return 0; + } + + int n = prices.length; + int f0 = -prices[0]; + int f1 = 0; + int f2 = 0; + for (int i = 1; i < n; ++i) { + int newf0 = Math.max(f0, f2 - prices[i]); + int newf1 = f0 + prices[i]; + int newf2 = Math.max(f1, f2); + f0 = newf0; + f1 = newf1; + f2 = newf2; + } + + return Math.max(f1, f2); + } + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/T26_MaxProfit.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/T26_MaxProfit.java new file mode 100644 index 0000000..7538939 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/T26_MaxProfit.java @@ -0,0 +1,102 @@ +package com.markilue.leecode.dynamic; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.dynamic + *@Author: dingjiawen + *@CreateTime: 2022-12-14 11:53 + *@Description: + * TODO 力扣714题 买卖股票的最佳时机含手续费: + * 给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。 + * 你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。 + * 返回获得利润的最大值。 + * 注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。 + *@Version: 1.0 + */ +public class T26_MaxProfit { + + /** + * 思路:本质上只是在卖出时需要付手续费: + * TODO 动态规划五部曲: + * (1)dp定义:dp[i][0]表示手上有股票的最大利润;dp[i][1]表示手上没有股票的最大利润 + * (2)dp状态转移方程: + * 1.dp[i][0]: + * dp[i][0]=max(dp[i-1][0],dp[i-1][1]-price[i]) + * 2.dp[i][1]: + * dp[i][1]=max(dp[i-1][1],dp[i-1][0]+price[i]-fee) + * (3)dp初始化:dp[0][0]=-price[i];dp[0][1]=0; + * (4)dp遍历顺序:从前往后 + * (5)dp举例推导:以prices = [1, 3, 2, 8, 4, 9], fee = 2为例 + * [0 1] + * i=0 -1 0 + * i=1 -1 0 + * i=2 -1 0 + * i=3 -1 5 + * i=4 1 5 + * i=5 1 8 + * 速度击败6.13%,内存击败80.17% 22ms + * @param prices + * @param fee + * @return + */ + public int maxProfit(int[] prices, int fee) { + + int[][] dp = new int[prices.length][2]; + dp[0][0]=-prices[0];dp[0][1]=0; + + for (int i = 1; i < prices.length; i++) { + dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]-prices[i]); + dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]+prices[i]-fee); + } + + return dp[prices.length-1][1]; + + } + + + /** + * 滚动数组优化 + * 速度击败87.26%,内存击败43.9% 3ms + * @param prices + * @param fee + * @return + */ + public int maxProfit1(int[] prices, int fee) { + + int dp0=-prices[0]; + int dp1=0; + + for (int i = 1; i < prices.length; i++) { + dp0=Math.max(dp0,dp1-prices[i]); + dp1=Math.max(dp1,dp0+prices[i]-fee); + } + + return dp1; + + } + + /** + * 官方贪心法: + * 速度击败100%,内存击败80.17% 3ms + * @param prices + * @param fee + * @return + */ + public int maxProfit2(int[] prices, int fee) { + int n = prices.length; + int buy = prices[0] + fee; + int profit = 0; + for (int i = 1; i < n; ++i) { + if (prices[i] + fee < buy) { + buy = prices[i] + fee; + } else if (prices[i] > buy) { + //因为可以买卖无数次,所以这是合理的,即可以当天买当天卖 + profit += prices[i] - buy; + //卖了之后必须重置价格,不然可以使用历史最少作为价格 + buy = prices[i]; + } + } + return profit; + } + +}