Leetcode [121][122][123][188][309][714] 买卖股票的最佳时机i ii iii iv 含冷冻期 含手续费 动态规划

这些题目具有共性,iv是最泛化的题目,所有其他题目都是iv的简化

 

 

 二、思路:

labuladong

这个问题的「状态」有三个,第一个是天数,第二个是当天允许交易的最大次数,第三个是当前的持有状态(即之前说的 rest 的状态,我们不妨用 1 表示持有,0 表示没有持有)。

我们用一个三维数组 dp 就可以装下这几种状态的全部组合,用 for 循环就能完成穷举:

 

 

 而且我们可以用自然语言描述出每一个状态的含义,比如说 dp[3][2][1] 的含义就是:今天是第三天,我现在手上持有着股票,至今最多进行 2 次交易。再比如 dp[2][3][0] 的含义:今天是第二天,我现在手上没有持有股票,至今最多进行 3 次交易。

想求的最终答案是 dp[n - 1][K][0],即最后一天,最多允许 K 次交易,所能获取的最大利润。读者可能问为什么不是 dp[n - 1][K][1]?因为 [1] 代表手上还持有股票,[0] 表示手上的股票已经卖出去了,很显然后者得到的利润一定大于前者。

状态转移方程:

 

 如果 buy,就要从利润中减去 prices[i],如果 sell,就要给利润增加 prices[i]。今天的最大利润就是这两种可能选择中较大的那个。而且注意 k 的限制,我们在选择 buy 的时候,把最大交易数 k 减小了 1

base case:

 

 把上面的状态转移方程总结一下:

 

 三、解决题目

1、K=1,只能进行一次买卖

根据 base case,可以做一些化简:

 

 

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

2、k = +infinity

如果 k 为正无穷,那么就可以认为 k 和 k - 1 是一样的。可以这样改写框架:

 

 

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

3、k=2

这里要考虑base情况,i=0时和k=0时

PS:这里k代表可以交易的最大次数。遍历中j代表进行了多少次交易

dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]+prices[i-1]),表示对于最大交易次数为j时,要么就是等于前一天进行交易次数为j时未持有股票的价值,要么等于前一天购买j次交易后持有股票并在i天卖掉的价值

dp[i][j][1]=max(dp[i-1][j][1],dp[i-1][j-1][0]-prices[i-1]),表示对于进行交易次数为j时持有股票的价值,要么等于前一天进行交易次数为j时持有股票的价值,要么等于前一天进行交易次数为j-1(保证之前最大交易次数为j-1,还有1次交易可以留到本次用)未持有股票,然后买了i天股票的价值

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        int k=2;
        vector<vector<vector<int>>> dp(n+1,vector<vector<int>>(k+1,vector<int>(2,0)));
        for(int i=0;i<=k;++i){
            dp[0][i][0]=0;
            dp[0][i][1]=INT_MIN;
        }
        for(int i=1;i<=n;++i){
            dp[i][0][0]=0;
            dp[i][0][1]=INT_MIN;
        }
        for(int i=1;i<=n;++i){
            for(int j=k;j>=1;--j){
                dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]+prices[i-1]);
                dp[i][j][1]=max(dp[i-1][j][1],dp[i-1][j-1][0]-prices[i-1]);
            }
        }
        return dp[n][k][0];
    }
};

4、就是通用的情况,k也为给定的参数

一次交易由买入和卖出构成,至少需要两天。所以说有效的限制次数 k 应该不超过 n/2,如果超过,就没有约束作用了,相当于 k = +infinity

k=min(k,n/2)

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int n=prices.size();
        vector<vector<vector<int>>> dp(n+1,vector<vector<int>>(k+1,vector<int>(2,0)));
        for(int i=0;i<=n;++i){
            dp[i][0][0]=0;
            dp[i][0][1]=INT_MIN;
        }
        for(int j=0;j<=k;++j){
            dp[0][j][0]=0;
            dp[0][j][1]=INT_MIN;
        }
        for(int i=1;i<=n;++i){
            for(int j=1;j<=k;++j){
                dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]+prices[i-1]);
                dp[i][j][1]=max(dp[i-1][j][1],dp[i-1][j-1][0]-prices[i-1]);
            }
        }
        return dp[n][k][0];
    }
};

5、包含冷冻期,卖出后间隔一天才可以买入

 

 

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        vector<vector<int>> dp(n+1,vector<int>(2,0));
        dp[0][0]=0;
        dp[0][1]=INT_MIN;
        for(int i=1;i<=n;++i){
            if(i==1){
                dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i-1]);
                dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i-1]);
                continue;
            }
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i-1]);
            dp[i][1]=max(dp[i-1][1],dp[i-2][0]-prices[i-1]);
        }
        return dp[n][0];
    }
};

6、包含手续费,每买卖一次需要交fee

每次交易要支付手续费,只要把手续费从利润中减去即可:

 

 

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int n=prices.size();
        vector<vector<long>> dp(n+1,vector<long>(2,0));
        dp[0][0]=0;
        dp[0][1]=INT_MIN;
        for(int i=1;i<=n;++i){
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i-1]-fee);
            dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i-1]);
        }
        return (int)dp[n][0];
    }
};

 

思路学习自labuladong公众号

posted @ 2021-05-13 11:04  鸭子船长  阅读(93)  评论(0编辑  收藏  举报