leetcode121 122 309 714 123 188股票买卖问题

参考labuladong的解析:https://labuladong.gitee.io/algo/1/13/

一、穷举框架

这 6 道题目是有共性的,我们只需要抽出来力扣第 188 题「 买卖股票的最佳时机 IV」进行研究,因为这道题是最泛化的形式,其他的问题都是这个形式的简化,看下题目:
在这里插入图片描述
动态规划核心套路 说过,动态规划算法本质上就是穷举「状态」,然后在「选择」中选择最优解。
那么对于这道题,我们具体到每一天,看看总共有几种可能的「状态」,再找出每个「状态」对应的「选择」。我们要穷举所有「状态」,穷举的目的是根据对应的「选择」更新状态。

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 为交易数的上限,01 代表是否持有股票。
此问题共 n × K × 2 种状态,全部穷举就能搞定。

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

而且我们可以用自然语言描述出每一个状态的含义,比如说 dp[3][2][1] 的含义就是:今天是第三天,我现在手上持有着股票,至今最多进行 2 次交易。再比如 dp[2][3][0] 的含义:今天是第二天,我现在手上没有持有股票,至今最多进行 3 次交易。很容易理解,对吧?
我们想求的最终答案是 dp[n - 1][K][0],即最后一天,最多允许 K 次交易,最多获得多少利润。

二、状态转移框架

现在,我们完成了「状态」的穷举,我们开始思考每种「状态」有哪些「选择」,应该如何更新「状态」。只看「持有状态」,可以画个状态转移图:
在这里插入图片描述
通过这个图可以很清楚地看到,每种状态(0 和 1)是如何转移而来的。根据这个图,我们来写一下状态转移方程:

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

边界:

dp[-1][...][0] = 0
解释:因为 i 是从 0 开始的,所以 i = -1 意味着还没有开始,这时候的利润当然是 0。
dp[-1][...][1] = -infinity
解释:还没开始的时候,是不可能持有股票的。
因为我们的算法要求一个最大值,所以初始值设为一个最小值,方便取最大值。
dp[...][0][0] = 0
解释:因为 k 是从 1 开始的,所以 k = 0 意味着根本不允许交易,这时候利润当然是 0。
dp[...][0][1] = -infinity
解释:不允许交易的情况下,是不可能持有股票的。
因为我们的算法要求一个最大值,所以初始值设为一个最小值,方便取最大值。

三、各个题目分析

在这里插入图片描述

------------属于k=1的情况------------
状态分析:由于最多只有一笔交易,所以第二个状态的取值只有1,所以第二个状态去掉了,且k = 0为边界,都是0
所以状态转移方程为:
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + v[i])
dp[i][1] = max(dp[i - 1][1], 0 - v[i])
-------------------------------------------
int maxProfit(vector<int>& v) {
        //初始化dp[0][1]为-price[0]
        int a = 0, b = -v[0], n = v.size();
        for (int i = 1; i < n; ++i) {
            int c = max(a, b + v[i]);
            int d = max(b, -v[i]);
            a = c;
            b = d;
        }
        return a;
    }

在这里插入图片描述

-----------属于k为正无穷的情况-------------
k没有存在的意义,所以把第二个状态省略掉
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + v[i])
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - v[i]);
------------------------------------
int maxProfit(vector<int>& v) {
        int a = 0, b = INT_MIN, n = v.size();
        for (int i = 0; i < n; ++i) {
            int c = max(a, b + v[i]);
            int d = max(b, a - v[i]);
            a = c;
            b = d;
        }
        return a;
    }

在这里插入图片描述

------------包含冷冻期的情况,leetcode122的变种题----------
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + v[i])
dp[i][1] = max(dp[i - 1][1], dp[i - 2][0] - v[i]);
-----------------------------------------
int maxProfit(vector<int>& v) {
        int n = v.size();
        vector<vector<int>> dp(n + 1, vector<int>(2));
        dp[0][0] = 0; dp[0][1] = INT_MIN;
        for (int i = 0; i < n; ++i) {
            dp[i + 1][0] = max(dp[i][0], dp[i][1] + v[i]);
            //dp[-2][0]为0,此时单独考虑
            if (i - 1 >= 0) dp[i + 1][1] = max(dp[i][1], dp[i - 1][0] - v[i]); 
            else dp[i + 1][1] = max(dp[i][1], -v[i]); 
        }
        return dp[n][0];
    }

在这里插入图片描述

------------包含手续费的情况,leetcode122的变种题----------
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + v[i])
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - v[i] - fee)
---------------------------------------
int maxProfit(vector<int>& v, int fee) {
        int a = 0, b = INT_MIN, n = v.size();
        for (int i = 0; i < n; ++i) {
            int c = max(a, b + v[i]);
            int d = max(b, a - v[i] - fee);
            a = c;
            b = d;
        }
        return a;
    }

在这里插入图片描述

------------k=2,就是之前的模板,k的取值为12-------------
边界:
dp[-1][][0]=0 dp[-1][][1]=无穷小
dp[i][0][0]=0 dp[i][0][1]=无穷小
转移方程:
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + v[i])
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - v[i])
----------------------------------------------
int maxProfit(vector<int>& v) {
        int n = v.size();
        int dp[n + 1][3][2];
        //i==-1的base:
        for (int i = 0; i < 3; ++i) {
            dp[0][i][0] = 0;
            dp[0][i][1] = INT_MIN;
        }
        //k==0的base:
        for (int i = 0; i < n; ++i) {
            dp[i + 1][0][0] = 0;
            dp[i + 1][0][1] = INT_MIN;
        }
        for (int i = 0; i < n; ++i) {
            for (int k = 1; k < 3; ++k) {
                dp[i + 1][k][0] = max(dp[i][k][0], dp[i][k][1] + v[i]);
                dp[i + 1][k][1] = max(dp[i][k][1], dp[i][k - 1][0] - v[i]);
            }
        }
        return dp[n][2][0];
    }

在这里插入图片描述

------------k=n,就是之前的模板,k的取值为1->n-------------
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + v[i])
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - v[i])
------------------------------------------------------------
int maxProfit(int k, vector<int>& v) {
        int n = v.size();
        int dp[n + 1][k + 1][2];
        for (int i = 0; i < k + 1; ++i) {
            dp[0][i][0] = 0;
            dp[0][i][1] = INT_MIN;
        }
        for (int i = 0; i < n; ++i) {
            dp[i + 1][0][0] = 0;
            dp[i + 1][0][1] = INT_MIN;
        }
        for (int i = 0; i < n; ++i) {
            for (int j = 1; j < k + 1; ++j) {
                dp[i + 1][j][0] = max(dp[i][j][0], dp[i][j][1] + v[i]);
                dp[i + 1][j][1] = max(dp[i][j][1], dp[i][j - 1][0] - v[i]);
            }
        }
        return dp[n][k][0];
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值