123.买卖股票的最佳时机III 188.买卖股票的最佳时机IV
123.买卖股票的最佳时机III
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
- 示例 1:
- 输入:prices = [3,3,5,0,0,3,1,4]
- 输出:6 解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3。
- 示例 2:
- 输入:prices = [1,2,3,4,5]
- 输出:4 解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4。注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
- 示例 3:
- 输入:prices = [7,6,4,3,1]
- 输出:0 解释:在这个情况下, 没有交易完成, 所以最大利润为0。
- 示例 4:
- 输入:prices = [1] 输出:0
提示:
- 1 <= prices.length <= 10^5
- 0 <= prices[i] <= 10^5
思路
思路:动态规划 第一次做这道题,真想不到如何定义dp数组 1.dp数组以及下标代表含义 定义二维dp数组dp[i][j] i表示在第i天最大的利益 一天一共五个状态 j = 0 没有持有股票 j = 1 第一次持有股票 j = 2 第一次持有股票后卖出 j = 3 第二次持有股票 j = 4 第二次持有股票后卖出 因为题目要求最多可买卖两次,当第二次持有股票时,需要知道【第一次持有股票】和【第一次持有股票卖出后】的状态 而之前的股票题目要么是完全不限制次数,要么只有一次,因此将【第一次持有股票卖出后】的状态和【没有持有股票】作为一个状态。需要注意下区分 2.确定递推公式 第一次持有股票 dp[i][1] = Math.max( dp[i-1][1] , -prices[i]) 第一次持有股票后卖出 dp[i][2] = Math.max( dp[i-1][2] , dp[i-1][1] + prices[i]) 第二次持有股票 dp[i][3] = Math.max( dp[i-1][3] ,dp[i-1][2] -prices[i]) 第二次持有股票后卖出 dp[i][4] = Math.max( dp[i-1][4] , dp[i-1][3] + prices[i]) 3.dp数组初始化 dp[0][0] = 0; dp[0][1] = -prices[0]; dp[0][2] = 0 可以理解当天买入,当天卖出,所以dp[0][2] = 0; dp[0][3] = -prices[i] 可以理解当天买入,当天卖出,再次买入 dp[0][4] = 0 4.遍历顺序,dp[i]由dp[i-1]推导,故正序 5.举例推导dp数组 由递推公式 时间复杂度o(n) 空间复杂度o(5*n)
代码如下
public static int maxProfitIII(int[] prices) {
if (prices == null || prices.length == 0)
return 0;
int[][] dp = new int[prices.length][5];
dp[0][0] = 0;
dp[0][1] = -prices[0];
dp[0][3] = -prices[0];
for (int i = 1; i < prices.length; i++) {
dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
}
return Math.max(dp[prices.length - 1][2], dp[prices.length - 1][4]);
}
问题
问题
采用了错误的思路思路:贪心
将利润拆分为(第二天 - 第一天),(第三天 - 第二天) 。。。。
将正利润累加,并存放在List中,求出利润最大的两项。
这种拆分利润的方式适用于求交易不限情况
无法通过此测试用例输入:[1,2,4,2,5,7,2,4,9,0] 输出:12 预期结果:13
list 中的结果 为3,5,7.那么其中两个最大的数字选择5 +7 =12作为结果
但测试用例中存在更大的利润组合
比如7-1 = 6,9-2 = 7
两者组合等于13
所以此思路不通过错误代码如下
public static int maxProfitIII(int[] prices) {
if (prices == null || prices.length == 0)
return 0;
int sum = 0;
List<Integer> profits = new LinkedList<>();
for (int i = 1; i < prices.length; i++) {
if (prices[i] - prices[i - 1] >= 0) {
sum = sum + prices[i] - prices[i - 1];
if (i == prices.length - 1)
profits.add(sum);
} else {
profits.add(sum);
sum = 0;
}
}
int maxProfit = 0;// 最大利润
int minProfit = 0;
for (int i = 0; i < profits.size(); i++) {
if (profits.get(i) > maxProfit) {
minProfit = maxProfit;
maxProfit = profits.get(i);
} else if (profits.get(i) > minProfit) {
minProfit = profits.get(i);
}
}
return maxProfit + minProfit;
}
188.买卖股票的最佳时机IV
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
- 示例 1:
- 输入:k = 2, prices = [2,4,1]
- 输出:2 解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2。
- 示例 2:
- 输入:k = 2, prices = [3,2,6,5,0,3]
- 输出:7 解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4。随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。
提示:
- 0 <= k <= 100
- 0 <= prices.length <= 1000
- 0 <= prices[i] <= 1000
思路
思路:动态规划 1.dp数组以及下标代表含义 定义二维dp数组dp[i][j] i表示在第i天最大的利益 一天一共2k + 1个状态 j = 0 没有持有股票 j = 1 第一次持有股票 j = 2 第一次持有股票后卖出 j = 3 第二次持有股票 j = 4 第二次持有股票后卖出 ..... j = 2*k -1 第k次持有股票 j = 2*k 第k次持有股票后卖出 2.确定递推公式 第一次持有股票 dp[i][1] = Math.max( dp[i-1][1] , -prices[i]) 第一次持有股票后卖出 dp[i][2] = Math.max( dp[i-1][2] , dp[i-1][1] + prices[i]) 第二次持有股票 dp[i][3] = Math.max( dp[i-1][3] ,dp[i-1][2] -prices[i]) 第二次持有股票后卖出 dp[i][4] = Math.max( dp[i-1][4] , dp[i-1][3] + prices[i]) 以此类推 3.dp数组初始化 dp[0][0] = 0; dp[0][1] = -prices[0]; dp[0][2] = 0 可以理解当天买入,当天卖出,所以dp[0][2] = 0; dp[0][3] = -prices[i] 可以理解当天买入,当天卖出,再次买入 dp[0][4] = 0 ....以此类推 4.遍历顺序,dp[i]由dp[i-1]推导,故正序 5.举例推导dp数组 由递推公式 时间复杂度o(n * k) 空间复杂度o(n * k)
代码如下
public static void main(String args[]) {
int k = 2;
int[] prices = new int[]{3, 2, 6, 5, 0, 3};
maxProfit(k, prices);
}
public static int maxProfit(int k, int[] prices) {
if (prices.length == 0)
return 0;
int[][] dp = new int[prices.length][2 * k + 1];
for (int i = 1; i <= k; i++) {
dp[0][2 * i - 1] = -prices[0];
}
for (int i = 1; i < prices.length; i++) {
for (int j = 1; j <= k; j++) {
dp[i][2 * j - 1] = Math.max(dp[i - 1][2 * j - 1], dp[i - 1][2 * j - 2] - prices[i]);
dp[i][2 * j] = Math.max(dp[i - 1][2 * j], dp[i - 1][2 * j - 1] + prices[i]);
}
}
return dp[prices.length - 1][2 * k];
}