股票交易类问题总结

 

股票交易类问题,要求在一定时间买入,然后卖出,以使利益最大化,通常采用动态规划求解。

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<int>& 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<int>& 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<int>& vs){
    int max_sum=INT_MIN;
    int sum=0;
    for(int i=0;i<vs.size();++i){
        sum+=vs[i];
        if(sum>max_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<int>& prices) {
        if(prices.empty())
            return 0;
        int n=prices.size();
        vector<vector<int>> dp(3,vector<int>(n,0));
        for(int k=1;k<=2;++k){
            int m=prices[0];
            for(int j=1;j<n;++j){
                m=min(m,prices[j]-dp[k-1][j-1]);
                dp[k][j]=max(dp[k][j-1],prices[j]-m);
            }
        }
        return dp[2][n-1];
    }
};

在交易只有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<int>& prices) {
        int n=prices.size();
    if(k==0 || n<2)
        return 0;
    if(k>n/2) k=n/2;
    vector<int> f(n,0);
    vector<int> pre(n,0);
    for(int t=1;t<=k;++t){
        int minv=prices[0];
        for(int i=1;i<n;++i){
            minv=min(minv,prices[i]-pre[i-1]);
            f[i]=max(prices[i]-minv,f[i-1]);
        }
        for(int i=0;i<n;++i)
            pre[i]=f[i];
    }

    return f[n-1];
    }
};

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<int>& prices) {
        if(prices.empty())
            return 0;
        int n=prices.size();
        vector<int> 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<n;++i){
            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];
        }
        return max(s0[n-1],s2[n-1]);
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值