zoukankan      html  css  js  c++  java
  • 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时

    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公众号

    联系方式:emhhbmdfbGlhbmcxOTkxQDEyNi5jb20=
  • 相关阅读:
    java的instanceof简单使用
    PHP中return,exit,die的区别
    C#调用Dll文件中方法的简单应用
    C#的托管和非托管的简单理解
    对象造型(引用类型转换)
    面向对象3大特性的简单理解
    css3自定义滚动条背景透明
    console.time方法与console.timeEnd方法
    IE8 input X 去掉文本框的叉叉和密码输入框的眼睛图标
    JavaSript模块规范
  • 原文地址:https://www.cnblogs.com/zl1991/p/14763697.html
Copyright © 2011-2022 走看看