动态规划DP(七) 股票交易

1.股票交易

在股票买卖的最佳时机问题中,给定一个数组,数组中的每个元素代表某一天的股票价格。你可以进行多次买入和卖出,但是必须在再次购买前卖出之前的股票。目标是找到最大的利润。

动态规划可以用于解决股票交易类的问题,其中最常见的问题是股票买卖的最佳时机问题(Best Time to Buy and Sell Stock)。

2.题目

1)

力扣icon-default.png?t=N658https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/找到当前位置之前的元素中的最小值,与当前位置的元素相减,遍历一维数组,找到最大差值。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int minp = prices[0];
        int n = prices.size();
        int res = 0;
        for(int i=1;i<n;i++){
            res = max(res, prices[i]-minp);
            minp = min(minp, prices[i]);
        }
        return res;
    }
};

2)

力扣icon-default.png?t=N658https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/这道题的难点在于最多购买股票两次,如果一直顺序考虑股票数组,就会变得很复杂,需要记录的值很多。

所有可以顺序加逆序考虑:

顺序:找到当前位置之前的最小值,当前位置元素与之计算差值,保留最大的差值。

逆序:找到当前位置之后的最大值,最大值与当前位置元素计算差值,保留最大的差值。

这样对于每个位置,都知道了这个位置之前一次购买卖出的最大利润,和这个位置以及这个位置之后一次购买卖出的最大利润。

把这个两个值相加,得到的就是每个位置最多购买两次彩票时的最大值。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();

        vector<int> dp(n,0);
        int minp = prices[0];
        for(int i=1;i<n;i++){
            dp[i] = max(dp[i-1], prices[i]-minp);
            minp = min(minp, prices[i]);
        }

        vector<int> dp2(n,0);
        int maxp = prices[n-1];
        for(int i=n-2;i>=0;i--){
            dp2[i] = max(dp2[i+1], maxp-prices[i]);
            maxp = max(maxp, prices[i]);
        }

        int res = 0;
        for(int i=0;i<n;i++){
            res = max(res, dp[i]+dp2[i]);
        }

        return res;
    }
};

3)

力扣icon-default.png?t=N658https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/题解:

这题需要额外考虑完成k次交易这一因素

所以转移方程为:buy[i][j]代表遍历prices[0...i]完成j笔交易,且此刻持有一只股票的最大收益。

sell[i][j]代表遍历prices[0...i]完成j笔交易,且此刻不持有股票的最大收益。

所以buy[i][j] = max(buy[i-1][j], sell[i-1][j]-prices[i])

取只考虑i-1只股票且完成j笔交易手上有股票的收益,或者只考虑i-1只股票且完成j笔交易购买当前这只股票收益

sell[i][j] = max(sell[i-1][j], buy[i-1][j-1]+prices[i])

取只考虑i-1只股票且完成j笔交易手上没有股票的收益,或者只考虑i-1只股票且此时卖出股票收益达到j笔交易的收益

这题好绕啊,建议自己码一下,会清晰很多。

代码:

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        vector<vector<int>> buy(n,vector<int>(k+1,INT_MIN/2));
        vector<vector<int>> sell(n,vector<int>(k+1,INT_MIN/2));
        if(2*k>=n) k = n/2;

        buy[0][0] = -prices[0];
        sell[0][0] = 0;
        
        for(int i=1;i<n;i++){
            sell[i][0] = 0;
            buy[i][0] = max(buy[i-1][0], sell[i-1][0]-prices[i]);
            for(int j=1;j<=k;j++){
                buy[i][j] = max(buy[i-1][j], sell[i-1][j]-prices[i]);
                sell[i][j] = max(sell[i-1][j], buy[i-1][j-1]+prices[i]);

            }
        }
        int res =  *(max_element(sell[n-1].begin(), sell[n-1].end()));
        return res>=0?res:0;
    }
};

因为是二维数组,且每次更新只用到[i-1]的值,所以肯定是可以压缩的。

压缩后,因为buy在sell前更新,所以 sell[i][j] = max(sell[i-1][j], buy[i-1][j-1]+prices[i])

就变成了 sell[i][j] = max(sell[i-1][j], buy[i][j-1]+prices[i])

又因为buy[i][j-1] = max(buy[i-1][j-1], sell[i-1][j-1]-prices[i]);

代入 sell[i][j],得到 sell[i][j] = max(sell[i-1][j], buy[i-1][j-1]+prices[i],sell[i-1][j-1] -prices[i]+prices[i])

比原本的 sell[i][j]多出了这一项:sell[i-1][j-1] -prices[i]+prices[i]

这一项表示在前i-1只股票中完成了j-1次交易,然后第j次交易是卖入第i只股票再在当天卖出。

所以也是sell[i-1][j]的一种特殊情况,所以多出的这一项不会比sell[i-1][j]大,也就不会影响最终的结果了。

所以是可以压缩数组的。

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        vector<int> buy(k+1,INT_MIN/2);
        vector<int> sell(k+1,INT_MIN/2);
        if(2*k>=n) k = n/2;

        buy[0] = -prices[0];
        sell[0] = 0;
        
        for(int i=1;i<n;i++){
            buy[0] = max(buy[0], sell[0]-prices[i]);
            for(int j=1;j<=k;j++){
                buy[j] = max(buy[j], sell[j]-prices[i]);
                sell[j] = max(sell[j], buy[j-1]+prices[i]);

            }
        }
        int res =  *(max_element(sell.begin(), sell.end()));
        return res>=0?res:0;
    }
};

速度明显变快了:

 4)

力扣icon-default.png?t=N658https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/

股票交易的题,最后代码都不复杂,但是设计转移方程的过程真的很绕。

这题多了一个冷冻期,也就是除了买和卖之外,又多了一个冷冻的状态。

dp[i]表示第i天结束之后的最大收益:

  • dp[i][0]表示第i天结束之后,手上持有一只股票
  • dp[i][1]表示第i天结束之后,手上不持有股票,且处于冷冻期
  • dp[i][1]表示第i天结束之后,手上不持有股票,不处于冷冻期

接下来分类讨论:

dp[i][0]表示第i天结束之后,手上持有一只股票

所以手上这只股票可以是今天买的,也可以是前一天就买了的

dp[i]][0] = max(dp[i-1][2]-prices[i], dp[i-1][0])

dp[i][1]表示第i天结束之后,手上不持有股票,且处于冷冻期

这表示第i天卖了股票,所以第i天结束之后,才会处于冷冻期

dp[i]][0] = dp[i-1][0]+prices[i]

dp[i][1]表示第i天结束之后,手上不持有股票,不处于冷冻期

第i天结束之后不处于冷冻期,那么前一天结束之后可以是冷冻期,也可以不是冷冻期

dp[i]][0] = max(dp[i-1][2], dp[i-1][1])

代码:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<vector<int>> dp(n, vector<int>(3,INT_MIN/2));
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        dp[0][2] = 0;

        for(int i=1;i<n;i++){
                dp[i][0] = max(dp[i-1][0], dp[i-1][2]-prices[i]);
                dp[i][1] = dp[i-1][0]+prices[i];
                dp[i][2] = max(dp[i-1][2], dp[i-1][1]);
           
        }
        return max(dp[n-1][1], dp[n-1][2]);
    }
};

转移方程中dp[i]只与dp[i-1]相关,所以可以压缩转移方程。简单地用六个变量来代替二维数组。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<vector<int>> dp(n, vector<int>(3,INT_MIN/2));
        int f0,f1,f2,f3,f4,f5;
        f0 = -prices[0];
        f1 = 0;
        f2 = 0;

        for(int i=1;i<n;i++){
            
                f3 = max(f0, f2-prices[i]);
                f4 = f0+prices[i];
                f5 = max(f2, f1);
                f0 = f3;
                f1 = f4;
                f2 = f5;
           
        }
        return max(f1,f2);
    }
};

速度快了很多:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值