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).
当遇到限制次数以及求最大的要求时,很自然要联想到动规。
动规中不同的状态设计,会有不同的时间复杂度。
本题有两种解法:
(1)
我最开始想到的是下面这种解法,但是Memory Limit Exceeced.
O(n^2)的解法
很自然的我们会想到记录从开头到第i个字符中进行k次交易能够得到的最大收益,记为dp[i][k].
进行更新:dp[i][k] = max{dp[j][k-1]+maxprofit(j...i)}, 0 <= j < i
这里我们需要用到每个可能的区间中进行一次交易的最大收益,也就是I中的问题。
预先计算出所有的maxprofit的复杂度是O(n^2),dp的复杂度也是O(n^2).
Code:
class Solution { public: int maxProfit(vector<int> &prices) { int n = prices.size(); if(n == 0) return 0; vector<vector<int>> dp(n, vector<int>(3,0)); vector<vector<int>> maxprofit(n, vector<int>(n, 0)); for(int i = 0; i < n; i++) { int curmin = prices[i]; int curprofit = 0; for(int j = i+1; j < n; j++) { if(prices[j] < curmin) curmin = prices[j]; else{ int gap = prices[j] - curmin; if(gap > curprofit) curprofit = gap; } maxprofit[i][j] = curprofit; if(i == 0) dp[j][1] = curprofit; } } for(int i = 1; i < n; i++) { int tmp = dp[0][1] + maxprofit[0][i]; for(int j = 1; j < i; j++) { if(dp[j][1] + maxprofit[j][i] > tmp) tmp = dp[j][1]+maxprofit[j][i]; } dp[i][2] = tmp; } return dp[n-1][2]; } };
(2)O(n)的解法
参考了这个:http://blog.csdn.net/linhuanmars/article/details/23236995
从上面的分析中我们很容易找出一个case, 例如有一个区间差异特别大,在很长时间内都是最优的选择,而我们的dp却需要不断的枚举一些不可能构成最终解的区间。
怎么样更聪明的定义状态呢?
上一种定义中,我们是定义(i,j)中的最大的解,这样不同的(i,j)对对应的可能是同一种解;这样我们可以用一个global变量来存储;但是由于处理的区间是不断延伸的,后面出现的数字可能和当前末尾的组合起来行程更大的区间,因此我们用一个local变量存储当前以i结尾的最大的解的值。
扩展到能够选择k个区间,我们定义:
global(i,k) 表示截止到第i天,进行k次交易能够获得的最优解(不一定以最后一天结束)
local(i,k) 表示以第i天结束的k次交易能够获得的最优解
能够得到递推式:
local[i][k] = max{global[i-1][k-1], local[i-1][k]} + prices[i] - prices[i-1];
global[i][k] = max{global[i-1][k], local[i][k]};
初始值全为零,最终解为global[n-1][2]。
Code:
class Solution { public: int maxProfit(vector<int> &prices) { int n = prices.size(); if(n == 0) return 0; vector<vector<int>> global(n, vector<int>(3,0)); vector<vector<int>> local(n, vector<int>(3,0)); for(int j = 1; j <= 2; j++) { for(int i = 1; i < n; i++) { local[i][j] = max(global[i-1][j-1], local[i-1][j]) + prices[i] - prices[i-1]; global[i][j] = max(global[i-1][j], local[i][j]); } } return global[n-1][2]; } };
从其他博客中找到了一种O(n)的解法,思路更简单,但是不具备从2次交易推广到k次的潜力。
主要的思路就是从前往后扫描一遍,找到从0到i的一次交易的最大收益,然后从后往前扫描一遍,得到从i+1到n-1的最大收益。然后两者相加取最大即可。
传送门:http://fisherlei.blogspot.com/2013/01/leetcode-best-time-to-buy-and-sell_3958.html