Problem
Notes
Best Time to Buy and Sell Stock系列第一题要求的最多一次交易的最大收益,Best Time to Buy and Sell Stock系列第二题要求的无限次交易的最大收益。此题为该系列第三题,要求了交易次数为两次。其实,要求为一次或无限次,求解很简单。但交易次数为k次(k为>1的常数),事情就变得困难起来,因为有了次数限制,我们必须面对很多子问题作出取舍,所以需要通过dp来求解。
自己一开始想的dp[i][j],i、j是表示两个售出的日子,搞得十分复杂也没搞出来。
下面的dp思路是借鉴他人内容
我们把dp[k][j]看作第k次交易在第j天时的最大收益。
那么我们首先得到初始化条件,即dp[0][0~n]=0,dp[k][0]=0,很好理解,第0次交易肯定无法获得利润吧,在第0天买卖肯定无法获得利润吧~之所以要维护第0次交易这个无实际意义的内容,是为了使后面的代码编写更方便。
然后确定递归方程dp[k][j]=max(dp[k][j-1],prices[j]-prices[i]+dp[k-1][i-1]),解释一下,如果第j天不买不卖,那么当天的利润肯定和前一天一样即dp[k][j-1],如果卖了,那么卖出价就是j这天的价格,买入价肯定就是j之前的某一天i的价格啦,除了这次获利,还要加上之前k-1次获利即dp[k-1][i-1] (这里可以发现我们初始化了dp[0][]的好处了,就是本来计算k=1时是要特判的,因为不存在k-1=0交易,但通过初始化dp[0]这一维都为0,就可以写成一种情况而不用特判了)。
这就是第j天可能发生的两种情况了,两者取最大就可以了,返回dp[2][n-1]就是最后的解。
version1的代码在“找买入价”这一步时,每次都需要在0~卖出日里把买入日找出来。这份代码是超时的。
version2优化了上述那一层循环。version1中可以明显发现,在version1中随着i增大,每次j都从头开始找,导致做了很多重复的工作,我们可以边更新dp数组然后边找0~i的最优买入日。
version3开始,我们着手与优化空间复杂度。version3所做的工作就是将k循环和i循环交换了次序。由于次序的交换,每个i都会对所有k值进行更新,相当于对于k进行类似并行的更新,所以我们需要记录下每个k对应的最小值。
version4。由version3可以明显看到,dp数组的第i次更新,只依赖于i-1这一项,那么我们就不需要记录所有的dp中间过程了。
Codes
version 1 //超时
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
if(n<=1) return 0;
int dp[3][n];
for(int i=0;i<n;++i) dp[0][i]=0;
for(int k=1;k<=2;++k)
{
dp[k][0]=0;
for(int i=1;i<n;++i)
{
int tempmin=prices[0];
// J循环为了找出在哪一天买入
for(int j=1;j<i;++j)
{
//如果没有初始化dp[0]这一维为0,k=1时要特判,因不存在k=0这次交易
tempmin=min(tempmin,prices[j]-dp[k-1][j-1]);
}
dp[k][i]=max(dp[k][i-1],prices[i]-tempmin);
}
}
return dp[2][n-1];
}
};
version 2 //优化,少了一重循环
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
if(n<=1) return 0;
int dp[3][n];
for(int i=0;i<n;++i) dp[0][i]=0;
for(int k=1;k<=2;++k)
{
dp[k][0]=0;
int tempmin=prices[0];
for(int i=1;i<n;++i)
{
dp[k][i]=max(dp[k][i-1],prices[i]-tempmin);
tempmin=min(tempmin,prices[i]-dp[k-1][i-1]);
}
}
return dp[2][n-1];
}
};
version3
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
if(n<=1) return 0;
int dp[3][n];
int m[3];
for(int i=0;i<n;++i) dp[0][i]=0;
for(int i=1;i<3;++i) dp[i][0]=0;
for(int i=1;i<3;++i) m[i]=prices[0];
for(int i=1;i<n;++i)
{
for(int k=1;k<3;++k)
{
dp[k][i]=max(dp[k][i-1],prices[i]-m[k]);
m[k]=min(m[k],prices[i]-dp[k-1][i-1]);
}
}
return dp[2][n-1];
}
};
version4
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
if(n<=1) return 0;
int dp[3];
int m[3];
for(int i=0;i<3;++i) dp[i]=0;
for(int i=1;i<3;++i) m[i]=prices[0];
for(int i=1;i<n;++i)
{
for(int k=1;k<3;++k)
{
dp[k]=max(dp[k],prices[i]-m[k]);
m[k]=min(m[k],prices[i]-dp[k-1]);
}
}
return dp[2];
}
};
Results
version2:
version4: