力扣题目链接(opens new window)
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
提示:
思路:动态规划 第一次做这道题,真想不到如何定义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;
}
力扣题目链接(opens new window)
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
提示:
思路:动态规划 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];
}