从买卖股票入手谈谈DP Table

动态规划问题主要就是要明确dp函数定义、搞清楚状态以及状态转移方程 

构建DP思路解析

状态

 188. 买卖股票的最佳时机 IV - 力扣(LeetCode)

对于股票,我们每天有三种选择 => buy, sell, hold

限制条件有 => 天数限制(n),买入股票次数限制(k)

状态(动态过程) => [第 i 天][还剩下 j 次买入机会][有无股票]

dp[i][k][0 or 1] 表示收益
0 <= i < n, 1 <= j <= k
n 为天数,大 K 为买入数的上限,0 和 1 代表是否持有股票。
此问题共 n × k × 2 种状态,全部穷举就能搞定。

for 0 <= i < n:
    for 1 <= j <= k:
        for s in {0, 1}:
            dp[i][j][s] = max(buy, sell, rest)

 最终所求状态 => dp[ n ][ k ][ 0 ] (最后不把股票抛出去不就是全亏嘛)

状态转移方程

根据每天的三种选择(buy, sell, hold

=> 两种状态转移:

1. dp[ i ][ j ][0](不持有股票) = max(hold, sell) = max(dp[i - 1][j][0], dp[i - 1][j][1] + price[i] );

2. dp[ i ][ j ][1] (持有股票) = max(hold, buy) = max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - price[i]);

考虑base case

1. 从天数角度 => 下标从0开始表示从第一天开始 => 和题目给的prices数组保持一致

(这里相当于把第一天的两种情况穷举出来了)

dp[0][...][0] = 0; 

dp[0][...][1] = -prices[0]; 

2. 从交易次数角度 => k = 0 表示不允许买股票,当然不可能持有股票 => NAN

dp[...][0][0] = 0;

dp[...][0][1] = NAN;(可以赋值INT_MIN方便max())

LeetCode实战

121. 买卖股票的最佳时机 - 力扣(LeetCode)

仅有一次买入机会

dp[i][1] = max(dp[i-1][1], -prices[i]);  =>  在选择在哪一天买入,在此之前没买过

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

 马尔可夫链 => 空间压缩

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

122. 买卖股票的最佳时机 II - 力扣(LeetCode)

无限次买入机会

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

空间压缩

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

123. 买卖股票的最佳时机 III - 力扣(LeetCode)

k = 2;

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

 空间压缩

先迭代k == 2的情况不会影响后面k == 1的迭代

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        // base case;
        int dp_10 = 0, dp_11 = -prices[0];
        int dp_20 = 0, dp_21 = -prices[0];
        for(int price: prices) {
            dp_20 = max(dp_20, dp_21 + price);
            dp_21 = max(dp_21, dp_10 - price);
            dp_10 = max(dp_10, dp_11 + price);
            dp_11 = max(dp_11, -price);
        }
        return dp_20;
    }
};

188. 买卖股票的最佳时机 IV - 力扣(LeetCode)

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        if(n <= 0) return 0;
        // 细分一下,可以认为退化为k无限的情况
        if(k > n/2) return InfinityK(prices); 
        vector<vector<vector<int>>> dp(n, vector<vector<int>>(k + 1, vector<int>(2)));
        // k = 0 的 base case
        for(int i = 0; i < n; i++) {
            dp[i][0][0] = 0;
            dp[i][0][1] = INT_MIN;
        }
        for(int i = 0; i < n; i++) {
            for(int j = k; j > 0; j--) {
                // i == 0 的 base case
                if(i == 0) {
                    dp[i][j][0] = 0;
                    dp[i][j][1] = -prices[i];
                    continue;
                }
                dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i]);
                dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i]);
            }
        }
    return dp[n-1][k][0];
    }

    int InfinityK(vector<int>& prices) {
        int n = prices.size();
        int dp_0 = 0, dp_1 = -prices[0];
        for(int i = 1; i < n; i++) {
            int temp = dp_0;
            dp_0 = max(dp_0, dp_1 + prices[i]);
            dp_1 = max(dp_1, temp - prices[i]);
        }
        return dp_0;
    }
};

其他变式

309. 最佳买卖股票时机含冷冻期 - 力扣(LeetCode)

卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天) => 修改状态转移方程

=> 持有股票:

dp[ i ][ j ][1]  = max(hold, buy) = max(dp[i - 1][j][1], dp[i - 2][j - 1][0] - price[i]);

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

714. 买卖股票的最佳时机含手续费 - 力扣(LeetCode)

有手续费不就相当于买股票总费用上升了么

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值