股票交易类问题,要求在一定时间买入,然后卖出,以使利益最大化,通常采用动态规划求解。
1.算法导论第三版投资挥发性公司即最大子数组问题(P39)
首先采用分治法,假定要寻找子数组A[low...high]的最大子数组,利用分治将数组划分为规模尽量相等的子数组A[low...mid],A[mid+1...high]。则A[low...high]的任何连续子数组所处位置必定是以下三种情况之一:
(1)完全位于子数组A[low...mid]中;
(2)完全位于子数组A[mid+1...high]中;
(3)跨越了中点。
前两种情况可以递归求解,关键在于寻找跨越中点的最大子数组,然后在三者中选最大的一个。由于必须跨越中点,所以可以在线性时间求解。代码如下:
int findMaxCrossSubArray(const vector& vs,int lo,int mid,int hi){//寻找跨越中点的最大子数组
int left_sum=INT_MIN,sum=0;
for(int i=mid;i>=lo;--i){
sum+=vs[i];
if(sum>left_sum)
left_sum=sum;
}
int right_sum=INT_MIN;
sum=0;
for(int i=mid+1;i<=hi;++i){
sum+=vs[i];
if(sum>right_sum)
right_sum=sum;
}
return left_sum+right_sum;
}
int findMaxSubArray(const vector& vs,int lo,int hi){//求最大子数组
if(lo==hi)
return vs[lo];
else{
int mid=(lo+hi)/2;
int left_sum=findMaxSubArray(vs,lo,mid);
int right_sum=findMaxSubArray(vs,mid+1,hi);
int cross_sum=findMaxCrossSubArray(vs,lo,mid,hi);
return max({left_sum,right_sum,cross_sum});
}
}
其次可以采用动态规划求解:从左到右遍历数组,记录目前为止最大子数组,若sum小于0则sum不可能在最大子数组内,否则去掉sum后的子数组值会更大,矛盾。
int findMaxSubArray2(const vector& vs){
int max_sum=INT_MIN;
int sum=0;
for(int i=0;imax_sum)
max_sum=sum;
if(sum<0)
sum=0;
}
return max_sum;
}
2.Best Time to Buy and Sell Stock III
(https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/description/)
首先,定义状态转移方程:
dp[k][i]表示第i天有k份交易的前提下能够获得的最大收益。
则dp[k][i]=max{dp[k][i-1],max{p[i]-p[j]+dp[k-1][j-1] (0<=j<=i)}}
据此,AC代码如下:
class Solution {
public:
int maxProfit(vector& prices) {
if(prices.empty())
return 0;
int n=prices.size();
vector> dp(3,vector(n,0));
for(int k=1;k<=2;++k){
int m=prices[0];
for(int j=1;j
在交易只有2份的情况下这种解法空间复杂度是O(n),若交易拓展至k份,则需要调整策略。
3.Best Time to Buy and Sell Stock IV
(https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/description/)
类似问题2,只是观察2中的解法可知,dp中的数据只有k-1和k次时间比i小一的才需要。因此,没有必要划分O(kn)的空间,只需要一个pre存储k-1次的交易,f存储当前交易即可。
class Solution {
public:
int maxProfit(int k, vector& prices) {
int n=prices.size();
if(k==0 || n<2)
return 0;
if(k>n/2) k=n/2;
vector f(n,0);
vector pre(n,0);
for(int t=1;t<=k;++t){
int minv=prices[0];
for(int i=1;i
4.Best Time to Buy and Sell Stock with Cooldown
(https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/description/)
这道题没有了交易次数的限制,但加上了冷冻期,即卖掉股票后需要冷冻一天才能再次购买。
可以定义三种状态以便于理解问题:
s0表示冷冻状态,s1表示购买,s2表示抛出股票。
则s0[i]=max(s0[i-1],s2[i-1])
s1[i]=max(s1[i-1],s0[i-1]-prices[i])
s2[i]=s1[i-1]+prices[i]
据此,代码如下:
class Solution {
public:
int maxProfit(vector& prices) {
if(prices.empty())
return 0;
int n=prices.size();
vector s0(n,0),s1(n,0),s2(n,0);
s0[0]=0;
s1[0]=-prices[0];
s2[0]=INT_MIN;
for(int i=1;i