团灭leetcode股票买卖问题

参考:https://labuladong.gitbook.io/algo/dong-tai-gui-hua-xi-lie/tuan-mie-gu-piao-wen-ti
状态:1. 哪一天,2. 允许交易的最大次数,3. 当前的持有状态(持有/没有持有)。
选择:买入,卖出,无操作

for 状态1 in 状态1的所有取值:
    for 状态2 in 状态2的所有取值:
        for ...
            dp[状态1][状态2][...] = 择优(选择1,选择2...)
//详细框架
dp[i][k][0 or 1]
0 <= i <= n-1, 1 <= k <= K
n 为天数,大 K 为最多交易数
此问题共 n × K × 2 种状态,全部穷举就能搞定。

for 0 <= i < n:
    for 1 <= k <= K:
        for s in {0, 1}:
            dp[i][k][s] = max(buy, sell, rest)     
///买卖股票 leetcode
base case && 状态转移方程
对某一天而言, 其状态指 buy/sell/rest 操作完成后的状态
base case:
dp[-1][k][0] = dp[i][0][0] = 0
dp[-1][k][1] = dp[i][0][1] = -infinity
 状态转移方程:
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])            
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
              max(   选择 rest  ,             选择 sell      )

解释:今天我没有持有股票,有两种可能:
要么是我昨天就没有持有,然后今天选择 rest,所以我今天还是没有持有;
要么是我昨天持有股票,但是今天我 sell 了,所以我今天没有持有股票了。

dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
              max(   选择 rest  ,           选择 buy         )

解释:今天我持有着股票,有两种可能:
要么我昨天就持有着股票,然后今天选择 rest,所以我今天还持有着股票;
要么我昨天本没有持有,但今天我选择 buy,所以今天我就持有股票了。

状态描述中的 k 表示:最多允许 k 次交易。所以说,如果我昨天没有持有,但是我今天要 buy,那么昨天最多允许 k-1 次交易。

///买卖股票1 leetcode 121
// dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]
// dp[i][1] = max(dp[i-1][1], - prices[i])
int maxProfit_k_1(vector<int>& prices) {
    ///directly
//    int n = prices.size();
//    if(n == 0) return 0;
//    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;
//        }
//        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];
    ///当前状态只与相邻状态有关, 可以降低空间复杂度到O(1)
    int n = prices.size();
    if(n == 0) return 0;
    // base case: dp[-1][0] = 0, dp[-1][1] = -infinity
    int dp_i_0 = 0; // is dp[-1][0]
    int dp_i_1 = INT_MIN; //include <climits>; it is dp[-1][1]
    for(int i = 0; i < n; i++) {
//      dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);
        dp_i_0 = max(dp_i_0, dp_i_1 + prices[i]);
//      dp[i][1] = max(dp[i-1][1], - prices[i]);
        dp_i_1 = max(dp_i_1, - prices[i]);
    }
    return dp_i_0;
}
///买卖股票2 leetcode 122
// k无穷大, k = k-1, 也就是说不需要记录 k 这个状态了:
//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])
int maxProfit_k_inf(vector<int>& prices) {
    int n = prices.size();
    if(n == 0) return 0;
    // base case: dp[-1][0] = 0, dp[-1][1] = -infinity
    int dp_i_0 = 0; // is dp[-1][0]
    int dp_i_1 = INT_MIN; //include <climits>; it is dp[-1][1]
    for(int i = 0; i < n; i++) {
        int temp = dp_i_0;  //dp_i_0会改变!
        dp_i_0 = max(dp_i_0, dp_i_1 + prices[i]);
        dp_i_1 = max(dp_i_1, temp - prices[i]);
    }
    return dp_i_0;
}
///买卖股票 有冷冻期 leetcode 309
//对某一天而言, 其状态指 buy/sell/rest 操作完成后的状态
//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])
//解释:第 i 天选择 buy 的时候,要从 i-2 的状态转移,而不是 i-1
//在第i天buy, 不可能在 i-1 天sell, 所以 i-1 天铁定没有收益
int maxProfit_cool(vector<int>& prices) {
    int n = prices.size();
    if(n == 0) return 0;
    // base case: dp[-1][0] = 0, dp[-1][1] = -infinity
    int dp_i_0 = 0; // dp[i-1][0], i is 0
    int dp_i_1 = INT_MIN; //include <climits>; dp[i-1][1], i is 0
    int pre = 0; // dp[i-2][0]
    for(int i = 0; i < n; i++) {
        int temp = dp_i_0;  //dp_i_0会改变!
        dp_i_0 = max(dp_i_0, dp_i_1 + prices[i]);
        dp_i_1 = max(dp_i_1, pre - prices[i]);
        pre = temp;
    }
    return dp_i_0;
}
///买卖股票 有手续费 leetcode 714
//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)
int maxProfit_fee(vector<int>& prices, int fee) {
    int n = prices.size();
    if(n == 0) return 0;
    // base case: dp[-1][0] = 0, dp[-1][1] = -infinity
    int dp_i_0 = 0; // is dp[-1][0]
    int dp_i_1 = INT_MIN; //include <climits>; it is dp[-1][1]
    for(int i = 0; i < n; i++) {
        int temp = dp_i_0;  //dp_i_0会改变!
        dp_i_0 = max(dp_i_0, dp_i_1 + prices[i]);
        dp_i_1 = max(dp_i_1, temp - prices[i] - fee);
    }
    return dp_i_0;
}
///买卖股票3 leetcode 123, k = 2
//dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
//dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
int maxProfit_k_2(vector<int>& prices) {
    int n = prices.size();
    if(n == 0) return 0;
    int max_k = 2;
    vector<vector<vector<int>>> dp(n, vector<vector<int>>(max_k+1, vector<int>(2)));
    //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 k = 1; k <= 2; k++) {
            if(i - 1 == -1) { // base case
                dp[i][k][0] = 0;
                dp[i][k][1] = -prices[i];
                continue;
            }
            dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i]);
            dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i]);
        }
    }
    return dp[n-1][max_k][0];
}
///买卖股票 k有值 leetcode 188
int maxProfit_k_any(vector<int>& prices, int k) {
    int n = prices.size();
    if(n == 0) return 0;
    int max_k = k;
    vector<vector<vector<int>>> dp(n, vector<vector<int>>(max_k+1, vector<int>(2)));
    //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 k = 1; k <= max_k; k++) {
            if(i - 1 == -1) { // base case
                dp[i][k][0] = 0;
                dp[i][k][1] = -prices[i];
                continue;
            }
            dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i]);
            dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i]);
        }
    }
    return dp[n-1][max_k][0];
}
int maxProfit(int k, vector<int>& prices) {
    int n = prices.size();
    if(k > n / 2) // k is inf
        return maxProfit_k_inf(prices);
    return maxProfit_k_any(prices, k);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值