From f5dd57cec562f48b89066db36d2813d6aac968d4 Mon Sep 17 00:00:00 2001 From: hk27xing <244798299@qq.com> Date: Thu, 8 Jul 2021 17:25:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E8=BF=9B123,=20188=E7=9A=84Java?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0123.买卖股票的最佳时机III.md | 60 +++++++++++------- .../0188.买卖股票的最佳时机IV.md | 63 +++++++++++-------- 2 files changed, 75 insertions(+), 48 deletions(-) diff --git a/problems/0123.买卖股票的最佳时机III.md b/problems/0123.买卖股票的最佳时机III.md index f6d2efb0..ad91c1aa 100644 --- a/problems/0123.买卖股票的最佳时机III.md +++ b/problems/0123.买卖股票的最佳时机III.md @@ -193,35 +193,49 @@ dp[1] = max(dp[1], dp[0] - prices[i]); 如果dp[1]取dp[1],即保持买入股 Java: ```java -class Solution { // 动态规划 +// 版本一 +class Solution { public int maxProfit(int[] prices) { - // 可交易次数 - int k = 2; + int len = prices.length; + // 边界判断, 题目中 length >= 1, 所以可省去 + if (prices.length == 0) return 0; - // [天数][交易次数][是否持有股票] - int[][][] dp = new int[prices.length][k + 1][2]; + /* + * 定义 5 种状态: + * 0: 没有操作, 1: 第一次买入, 2: 第一次卖出, 3: 第二次买入, 4: 第二次卖出 + */ + int[][] dp = new int[len][5]; + dp[0][1] = -prices[0]; + // 初始化第二次买入的状态是确保 最后结果是最多两次买卖的最大利润 + dp[0][3] = -prices[0]; - // badcase - dp[0][0][0] = 0; - dp[0][0][1] = Integer.MIN_VALUE; - dp[0][1][0] = 0; - dp[0][1][1] = -prices[0]; - dp[0][2][0] = 0; - dp[0][2][1] = Integer.MIN_VALUE; - - for (int i = 1; i < prices.length; i++) { - for (int j = 2; j >= 1; j--) { - // dp公式 - dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]); - dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]); - } + for (int i = 1; i < len; i++) { + dp[i][1] = Math.max(dp[i - 1][1], -prices[i]); + dp[i][2] = Math.max(dp[i - 1][2], dp[i][1] + prices[i]); + dp[i][3] = Math.max(dp[i - 1][3], dp[i][2] - prices[i]); + dp[i][4] = Math.max(dp[i - 1][4], dp[i][3] + prices[i]); } - int res = 0; - for (int i = 1; i < 3; i++) { - res = Math.max(res, dp[prices.length - 1][i][0]); + return dp[len - 1][4]; + } +} + +// 版本二: 空间优化 +class Solution { + public int maxProfit(int[] prices) { + int len = prices.length; + int[] dp = new int[5]; + dp[1] = -prices[0]; + dp[3] = -prices[0]; + + for (int i = 1; i < len; i++) { + dp[1] = Math.max(dp[1], dp[0] - prices[i]); + dp[2] = Math.max(dp[2], dp[1] + prices[i]); + dp[3] = Math.max(dp[3], dp[2] - prices[i]); + dp[4] = Math.max(dp[4], dp[3] + prices[i]); } - return res; + + return dp[4]; } } ``` diff --git a/problems/0188.买卖股票的最佳时机IV.md b/problems/0188.买卖股票的最佳时机IV.md index 937aee3a..39950fe0 100644 --- a/problems/0188.买卖股票的最佳时机IV.md +++ b/problems/0188.买卖股票的最佳时机IV.md @@ -170,41 +170,54 @@ public: Java: ```java -class Solution { //动态规划 +// 版本一: 三维 dp数组 +class Solution { public int maxProfit(int k, int[] prices) { - if (prices == null || prices.length < 2 || k == 0) { - return 0; + if (prices.length == 0) return 0; + + // [天数][交易次数][是否持有股票] + int len = prices.length; + int[][][] dp = new int[len][k + 1][2]; + + // dp数组初始化 + // 初始化所有的交易次数是为确保 最后结果是最多 k 次买卖的最大利润 + for (int i = 0; i <= k; i++) { + dp[0][i][1] = -prices[0]; } - // [天数][交易次数][是否持有股票] - int[][][] dp = new int[prices.length][k + 1][2]; - - // bad case - dp[0][0][0] = 0; - dp[0][0][1] = Integer.MIN_VALUE; - dp[0][1][0] = 0; - dp[0][1][1] = -prices[0]; - // dp[0][j][0] 都均为0 - // dp[0][j][1] 异常值都取Integer.MIN_VALUE; - for (int i = 2; i < k + 1; i++) { - dp[0][i][0] = 0; - dp[0][i][1] = Integer.MIN_VALUE; - } - - for (int i = 1; i < prices.length; i++) { - for (int j = k; j >= 1; j--) { - // dp公式 + for (int i = 1; i < len; i++) { + for (int j = 1; j <= k; j++) { + // dp方程, 0表示不持有/卖出, 1表示持有/买入 dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]); dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]); } } + return dp[len - 1][k][0]; + } +} - int res = 0; - for (int i = 1; i < k + 1; i++) { - res = Math.max(res, dp[prices.length - 1][i][0]); +// 版本二: 空间优化 +class Solution { + public int maxProfit(int k, int[] prices) { + if (prices.length == 0) return 0; + + // [天数][股票状态] + // 股票状态: 奇数表示第 k 次交易持有/买入, 偶数表示第 k 次交易不持有/卖出, 0 表示没有操作 + int len = prices.length; + int[][] dp = new int[len][k*2 + 1]; + + // dp数组的初始化, 与版本一同理 + for (int i = 1; i < k*2; i += 2) { + dp[0][i] = -prices[0]; } - return res; + for (int i = 1; i < len; i++) { + for (int j = 0; j < k*2 - 1; j += 2) { + dp[i][j + 1] = Math.max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]); + dp[i][j + 2] = Math.max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]); + } + } + return dp[len - 1][k*2]; } } ```