【题目描述】
给定一个数组,它的第
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 <= 105
0 <= prices[i] <= 105
【题目链接】:. - 力扣(LeetCode)
【解题代码】
package dp;
public class MaxProfit3 {
public static void main(String[] args) {
//int[] prices = {1, 2, 3, 4, 5};
int[] prices = {2, 1, 2, 0, 1};
int result = new MaxProfit3().maxProfit(prices);
System.out.println("计算结果:" + result);
}
public int maxProfit(int[] prices) {
int[][][] dp = new int[prices.length][2][3];
dp[0][0][0] = 0;
dp[0][0][1] = 0;
dp[0][0][2] = 0;
dp[0][1][0] = -prices[0];
dp[0][1][1] = -prices[0];
dp[0][1][2] = -prices[0];
for (int i = 1; i < prices.length; ++i) {
dp[i][0][0] = 0;
dp[i][0][1] = Math.max(dp[i - 1][0][1], dp[i - 1][1][0] + prices[i]);
dp[i][0][2] = Math.max(dp[i - 1][0][2], dp[i - 1][1][1] + prices[i]);
dp[i][1][0] = Math.max(dp[i - 1][1][0], dp[i - 1][0][0] - prices[i]);
dp[i][1][1] = Math.max(dp[i - 1][1][1], dp[i - 1][0][1] - prices[i]);
dp[i][1][2] = Math.max(dp[i - 1][1][2], dp[i - 1][0][2] + prices[i]);
}
return dp[prices.length - 1][0][2];
}
}
【解题思路】
之前做了相关题目买卖股票的最佳时机和买卖股票的最佳时机II,并都写了博客做了分析,以为这道题应该不难,结果折腾了好久还是没有思路。昨天在做完买卖股票的最佳时机含手续费时,突然有了思路:之前的题目根据当天有无股票的情况,将一维dp数组扩展为二维数组 那么按照这个思路,是否可以再根据当天的投资次数对二维数组再次扩展然后计算呢,按照这个思路并进行尝试终于得到如下算法思路:
- 定义三维动态规划数组dp[n][2][3],第一维是天数,第二维是手上是否有股票,第三维是交易次数(0次,1次,2次);接下來对每天各种情况的收益值进行一一分析:
- 今天手里没有股票,且0次交易的最大获利肯定0;
- 今天手里没有股票,且1次交易的最大获利等于:昨天手里没有股票,且有一次交易和昨天0次交易且今天卖掉股票两者最大值
- 今天手里没有股票,且2次交易的最大获利等于:昨天手里没有股票且有两次交易,和昨天1次交易且今天卖掉股票两者最大值
- 今天手里有股票,且0次交易的最大获利等于:昨天手里股票且有1次交易,和昨天1次交易且今天买股票两者最大值
- 今天手里有股票,且1次交易的最大获利等于:昨天手里有股票且有1次交易,和昨天没有股票且有1次交易且今天买股票的两者最大值
按照这个思路,很快完成代码编写,编译运行并提交成功
【解题步骤】
- 定义三维动态规划数组dp
int[][][] dp = new int[prices.length][2][3];
- 初始化第1天各种情况的最大获利值
dp[0][0][0] = 0; dp[0][0][1] = 0; dp[0][0][2] = 0; dp[0][1][0] = -prices[0]; dp[0][1][1] = -prices[0]; dp[0][1][2] = -prices[0];
- 然后从第二天开始到最后一天依次计算各种情况的最大获利值
for (int i = 1; i < prices.length; ++i) { // 今天手里没有股票,且0次交易的最大获利0 dp[i][0][0] = 0; // 今天手里没有股票,且一次交易的最大获利:昨天手里没有股票,且有一次交易和昨天0次交易今天卖掉股票两者最大值 dp[i][0][1] = Math.max(dp[i - 1][0][1], dp[i - 1][1][0] + prices[i]); // 今天手里没有股票,且两次交易的最大获利:昨天手里没有股票且有两次交易,和昨天1次交易今天卖掉股票两者最大值 dp[i][0][2] = Math.max(dp[i - 1][0][2], dp[i - 1][1][1] + prices[i]); // 今天手里有股票,且0次交易的最大获利:昨天手里股票且有1次交易,和昨天1次交易今天买股票两者最大值 dp[i][1][0] = Math.max(dp[i - 1][1][0], dp[i - 1][0][0] - prices[i]); // 今天手里有股票,且1次交易的最大获利:昨天手里有股票且有1次交易,和昨天没有股票且有1次交易然后今天买股票的两者最大值 dp[i][1][1] = Math.max(dp[i - 1][1][1], dp[i - 1][0][1] - prices[i]); // 今天手里有股票,且2次交易的最大获利:昨天手里有股票且有2次交易,和昨天有股票,且1次交易然后今天卖掉股票两者最大值 dp[i][1][2] = Math.max(dp[i - 1][1][2], dp[i - 1][0][2] + prices[i]); }
- 最后一天手里没有股票且交易两次的最大获利值就是最终结果
return dp[prices.length - 1][0][2];
【思考总结】
- 动态规划算法是数据结构中一个很重要的算法,另外一般学校里教的都很浅薄,程序员一定要自学掌握其算法脉络,并能学会灵活运用;
- 无后效性,递推算法一般只需要考虑眼前最近一层,其它层一定不需要考虑。“未来与过去无关”;
- 这道题的关键点就是:根据当天的投资次数将二维数组扩展成三维数组,并依次根据前一天的计算数值,对每天的各种情况进行计算;
- LeetCode解题之前,一定不要看题解,看了就“破功”了!