[Mdp] lc122. 买卖股票的最佳时机 II(dp+贪心+状态机+思维)

本文介绍了一种股票交易策略,通过分析股票价格变化,采用动态规划思想,实现股票买卖的最大化收益。文章详细阐述了如何将复杂的大区间交易分解为简单的小区间交易,并提供了具体的算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 题目来源

链接:lc122. 买卖股票的最佳时机 II

股票问题合集:
[Edp] lc121. 买卖股票的最佳时机(dp)
[Mdp] lc122. 买卖股票的最佳时机 II(dp+贪心+状态机+思维)
[Mdp] lc309. 买卖股票的最佳时机含冷冻期(状态机dp+股票买卖模型+经典问题)
[Hdp] lc123. 买卖股票的最佳时机 III(dp+前后缀分解)
[Hdp] lc188. 买卖股票的最佳时机 IV(状态机模型dp+线性dp+空间优化)

2. 题目解析

该股票问题:可以买卖无限次,每笔交易不能重叠(指买入下支股票前需要将手中股票卖出)

思路:

  • 理解一个买入卖出方式即能解决该问题
  • 第 3 天买入,第 6 天卖出,那么价值为 v 6 − v 3 v6 - v3 v6v3,其等价于 v 4 − v 3 + v 5 − v 4 + v 6 − v 5 = v 6 − v 3 v4- v3 +v5 - v4 + v6 - v5=v6-v3 v4v3+v5v4+v6v5=v6v3
  • 抽象来讲即为:如果 i i i 天买入 j j j 天卖出,那么所获利润为 p j − p i p_j-p_i pjpi,这个可以分解为 p i + 1 − p i + p i + 2 − p i + 1 + . . . + p j − 1 − p j − 2 − p j − 1 = p j − p i p_{i+1} - p_i + p_{i+2} - p_{i+1} +...+p_{j -1} - p_{j - 2} - p_{j -1} = p_j-p_i pi+1pi+pi+2pi+1+...+pj1pj2pj1=pjpi 这两者也是等价的
  • 故我们相当于将一个区间段的交易分解为每一个小区间之间的交易总和。这里的区间指的是跨越天数的股票买卖,这里的小区间就是当天买入,次天卖出这样的操作。每一个区间段交易均可以这样分解
  • 那么问题转化为:当天买入,次天卖出(这就是小区间)时
    • 收益如果为正,意味着这次操作就可以获利,那么隔很多天买卖股票的操作就可以包含这个小区间,这段区间对最后收益贡献一个正值
    • 收益如果为负,那么如果我们大区间包含了该段区间势必导致我们获利减小,故需要跳过该小区间,这段区间对最后收益贡献为 0
  • 这些小区间收益为正的操作就是最优操作,其中能够首尾相连起来的就可以将其合并成一个大区间

这个转化思想还是挺重要的,留意。

总结: res += max{0,第 i 天的价格 - 前 i - 1 天的价格}


同时,本题的通解其实是需要考虑状态机 dp 的,类似于:[Hdp] lc188. 买卖股票的最佳时机 IV(状态机模型dp+线性dp+空间优化) 中的状态机 dp 模型一样。

思路:状态机dp

  • i 天来讲,有两种状态:持有、未持有 股票。

  • i-1i 的话,共有四种状态:

  • 持有–》持有 无操作

  • 未持有–》未持有 无操作

  • 持有–》未持有:以当前价格卖出

  • 未持有–》持有:以当前价格买入

  • 借用一下灵神算法基础课第 21 课所讲:

    • 在这里插入图片描述
  • 设:f[i][0] 表示持有股票,f[i][1] 表示未持有股票

  • 初始化时有,f[0][0] = -p[i],相当于第 i 天买入了股票。f[0][0] = 0 相当于没股票,没操作。


贪心思路:

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

状态机dp 思路:

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

2025年02月08日21:53:33
上面确实很容易理解,golang 版本如下:

func maxProfit(prices []int) int {
    n := len(prices)
    f := make([][]int, n)
    for i := 0; i < n; i ++ {
        f[i] = make([]int, 2)
    }

    f[0][0] = -prices[0]
    f[0][1] = 0
    for i := 1; i < n; i ++ {
        f[i][0] = max(f[i - 1][0], f[i - 1][1] - prices[i])
        f[i][1] = max(f[i - 1][1], f[i - 1][0] + prices[i])
    }

    return max(f[n - 1][0], f[n - 1][1])
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值