Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete at most two transactions.
Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).
Example 1:
Input: [3,3,5,0,0,3,1,4] Output: 6 Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3. Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1 = 3.
题目分析:在最多两次交易的前提下取得最大利益。在前面的题目中 122.Best Time to Buy and Sell Stock II没有交易次数的限制,因此我们可以每次低点买入,高点卖出。但在次数限制下这样就不可行了,例如:
[1,2,4,2,5,7,2,4,9,0]
若不限制次数,则在第一天买入第三天卖出,收益3;在第四天买入第六天卖出,收益5;在第七天买入第九天卖出,收益7。
直观的想法是取收益最大的两次,但这样的收益是5+6=11;而最优解是在第一天买入第六天卖出,在第七天买入第九天卖出,收益为6+7=13。
因此考虑用动态规划,引入数组max_profit[i][j],表示到第i天为止交易j次得到的最大收益。考虑递归公式:
max_profit[i][j]=max(max_profit[i-1][j],max_profit[i-1][j-1]+prices[i]-prices[i-1]);
然而分析发现,max_profit[i-1][j]还要细分为在第i-1天卖出及在第i-1天不交易两种情况。
1、在第i-1天卖出时,可能在第i天卖出收益更大,即max_profit[i-1][j]+prices[i]-prices[i-1],也可能就是第i-1天卖出收益大,即max_profit[i-1][j]。
2、第i-1天不交易,即最后一笔交易在第i-1天之前完成。
则上面的表达式已经不能区分不同的情况,因此引入两个数组变量,jmax_profit[i][j]表示到第i天为止交易j次并且最后一笔交易是在第i天卖出的最大收益;max_profit[i][j]表示到第i天为止交易j次的最大收益。
考虑递归公式:
jmax_profit[i][j]=max(jmax_profit[i-1][j],max_profit[i-1][j-1])+prices[i]-prices[i-1];
max_profit[i][j]=max(jmax_profit[i][j],max_profit[i-1][j]);
其中jmax_profit[i][j]要求最后一次交易在第i天完成,那么:
1、倒数第二次交易可能是在第i-1天之前完成,再加上第i-1天买入第i天卖出的收益,即 max_profit[i-1][j-1]+prices[i]-prices[i-1];
2、倒数第一次交易可能是在第i-1天完成,再加上第i-1天买入第i天卖出的收益,即jmax_profit[i-1][j]+prices[i]-prices[i-1];表示此次交易在第i天卖出收益比在第i-1天卖出收益更大。
而max_profit[i][j]只要能收益最大即可,那么:
1、最后一次交易在第i天完成,即jmax_profit[i][j];
2、最后一次交易在第i-1天及之前完成,即max_profit[i-1][j]);
代码如下:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
if(n==0) return 0;
int jmax_profit[n][3]={0};
int max_profit[n][3]={0};
for(int i=1;i<n;i++){
for(int j=1;j<3;j++){
jmax_profit[i][j]=max(jmax_profit[i-1][j],max_profit[i-1][j-1])+prices[i]-prices[i-1];
max_profit[i][j]=max(jmax_profit[i][j],max_profit[i-1][j]);
}
}
return max_profit[n-1][2];
}
};
在网上看到了大神的算法,可以用两个一维数组完成计算:
考虑之前的递归公式:
jmax_profit[i][j]=max(jmax_profit[i-1][j],max_profit[i-1][j-1])+prices[i]-prices[i-1];
max_profit[i][j]=max(jmax_profit[i][j],max_profit[i-1][j]);
想要将jmax_profit[i][j]变为jmax_profit[j],将max_profit[i][j]变为max_profit[j]。如果用一维数组,只能保存当前一行的内容,在计算jmax_profit[j]时需要用到[i-1][j-1]的内容,若j从前往后,那么到了i,j的时候,[i-1][j-1]会被[i][j-1]覆盖掉,所以我们将j从后往前计算。
代码如下:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
if(n==0) return 0;
int jmax_profit[3]={0};
int max_profit[3]={0};
for(int i=1;i<n;i++){
for(int j=2;j>=1;j--){
jmax_profit[j]=max(jmax_profit[j],max_profit[j-1])+prices[i]-prices[i-1];
max_profit[j]=max(jmax_profit[j],max_profit[j]);
}
}
return max_profit[2];
}
};
相似题目:Leetcode 121. Best Time to Buy and Sell Stock
Leetcode 122. Best Time to Buy and Sell StockII