这道题目可以算是一道经典而又比较简单的动态规划问题了。
根据算法导论中的介绍,我们可以先来分析一下最优解的构成:两次交易没有重合区域,所以可以假定一个分界线,分界线左边的解是左边部分的最优解,而分界线右边部分的解也是最优解。求得这两个区域最优解的办法,就是股票买卖最佳时机1这道题目的解法。
所以说,为找到问题的答案,我们需要确定两个“最优”,先找到分界线的位置,再算出分界线左右两边的最优解。
如果我们暴力循环的话,这个解法就是可行的。时间复杂度为O(N^2),第一层循环遍历分界线所有的位置,第二层循环寻找最优解。这里注意,求解动态规划问题,这一步是不可跳过的!动态规划,本质上就是对于循环之类已有的解法进行优化,而不是凭空出来的解法。求解动态规划类问题,最好的思路就是不考虑时间复杂度,先找到一个可行的解法,再对它进行优化。
然后分析这个过程,直觉告诉我们这个解法存在重复计算,然后我们在脑海中推一下运算轨迹。这里再次注意,不要把求解子问题的方法看作一个已经封装好的函数。因为这个优化是对整个运算过程进行的。
我们分析一下求解子问题的方法,求得最优解需要前一个问题的最优解,这样我们如果我们要求出长为a的数组的最优解,我们就必须有长度为a-1的数组的最优解。
这时我们观察右边,右边只要我们把计算“股票买卖最佳时间1”的解法反过来,变成必须先卖后买,就可以从小到大在O(n)时间之内填好这个表。(这对应时间的对称性???)
在纸上画出调用关系图来,就是差不多这个样子,我们接着分析储存中间结果的方法,显然,一个简单的数组就可以存储这些中间结果。我们用一个循环先把数组填满,然后再遍历一次数组找到i,就可以在线性时间复杂度O(n)之内求出问题的解。
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size()==0) return 0;
int rpros[prices.size()];
int rmax=0;
int min=prices[0];
int rpro=0;
int profit=0;
for(int i=prices.size()-1;i>=0;i--){
if(prices[i]>rmax)
rmax=prices[i];
else if(rmax-prices[i]>rpro)
rpro=rmax-prices[i];
rpros[i]=rpro;
}
for(int i=0;i<prices.size();i++){
if(prices[i]<min) min=prices[i];
else if(prices[i]-min+rpros[i]>profit)
profit=prices[i]-min+rpros[i];
}
return profit;
}
};
最后结果是87%,说明这个解法还是不错的,我不知道还有没有更优的解法,毕竟O(n)我感觉已经是这道题的极限了。