题目:
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 (ie, you must sell the stock before you buy again).
代码:
class Solution { public: int maxProfit(vector<int>& prices) { if (prices.empty()) return 0; const int len = prices.size(); vector<int> l(len,0),r(len,0); // from left to right l[0] = 0; int l_min = prices[0]; for ( int i = 1; i < l.size(); ++i ) { l_min = std::min(l_min, prices[i]); l[i] = std::max(l[i-1], prices[i]-l_min); } // from right to left r[len-1] = 0; int r_max = prices[len-1]; for ( int i = len-1; i >= 0; --i ) { r_max = std::max(r_max, prices[i]); r[i] = std::max(r[i-1], r_max-prices[i]); } // travseral the best two times int max_profit = 0; for ( int i = 0; i < prices.size(); ++i ) { max_profit = std::max(max_profit, l[i]+r[i]); } return max_profit; } };
tips:
此题说最多交易两次,求最大获利。
直觉的想法就是,把整个时间段分割成两部分( 共有prices.size()种分类方法 );分好后分别求两部分各自的最大值;这种算法是O(n²)时间复杂度的。
模仿之前求过的largest rectangle in histogram这道题的思路,能否利用dp思想,把算过的中间结果都存起来,把时间复杂度降低到O(n)。
想到这个思路就比较明确了:
1. 从左向右走一遍,l[i]存放0~i最多交易1次获利最大的值
2. 从右向左走一遍,r[i]存放i~prices.size()-1最多交易一次获利的最大值
3. 遍历数组l和数组r,通过遍历每种分割情况下的最大获利,并最终获得最终的最大获利值。
这里还有个细节可能会产生疑义:如果以第i天作为分割点,那么这第i天是算到前半截还是后半截呢?
这里分两种情况:
1. 如果“前半截的最大利润”和“后半截的最大利润”只有一截涉及到了第i天,显然l[i]+r[i]这个算法是没问题的
2. 如果“前”、“后”两截都涉及到了第i天呢?这时候有两种理解方法:
2.1 前后交易两次:前半截的某一天买入,第i天卖了,挣一笔;第i天卖完又买入了,到后面的某一天又卖了,挣第二笔。两笔加起来最大。
2.2 前后交易一次:前半截的某一天买入,第i天虽然卖了获利最大,但是不卖,留着;等到后面的某一天发现获利最大,直接挣一笔最大的,同样获利最大。
因此,无论按照哪种理解方法,l[i]+r[i]都是合理的,不会因为第i天作为分割点而产生影响。