我爱学算法之——动态规划(四)

一、买卖股票的最佳时机含冷冻期

题目解析

给定一个整数数组 prices,其中prices[i]表示第 i 天的股票价格;计算 最大收益

约束条件

  • 不能同时参与多笔交易(已有股票就不能再买入了)
  • 卖出股票后,有一天的冷静期

算法思路

状态表示

对于这道题,第 i 天结束 存在三种状态:持有股票(买入)不持有股票,处于冷冻期(冷冻)不持有股票,不处于冷冻期(可交易)

所以,这里就需要表示三种状态下的最大收益:

  • dp[i][0] : 第 i 天结束处于 买入 状态,此时的最大收益
  • dp[i][1] : 第 i 天结束处于 冷冻 状态,此时的最大收益
  • dp[i][2] : 第 i 天结束处于 可交易 状态,此时的最大收益

状态转移方程

这里只靠想象,去找这个状态转移还是比较费劲的,可以自己画一个图,思考状态之间是如何转化的:

如上图,状态转移清晰可见,状态转移方程:

  • dp[i][0] = max(dp[i-1][0],dp[i-1][2] - prices[i])
  • dp[i][1] = dp[i-1][0] + prices[i]
  • dp[i][2] = max(dp[i-1][2],dp[i-1][1])

初始化

这里初始化dp[0][0]dp[0][1]dp[0][2],第一结束处于买入、冷冻、可交易状态的最大收益即可。

返回值

题目要求的是:最大利润,最后一天(n-1)再持有股票(买入状态)没有意义;所以,最终结果应该是 dp[n-1][1]dp[n-1][2]中的最大值。

代码实现

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

二、买卖股票的最佳时机含手续费

题目解析

给定一个数组 proces和一个整数feeproces[i]表示第 i 天的股票价格;fee表示交易股票的手续费用(每笔交易只需支付一次)

约束条件

  • 不能同时参与多笔交易(已有股票就不能再买入了)
  • 每笔交易需要支付手续费用

算法思路

状态表示

这道题目,第 i 天结束,只有两种状态:持有股票、不持有股票

  • dp[i][0] : 表示第 i 天结束,持有股票时的最大收益
  • dp[i][1] : 表示第 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] - fee)

初始化

这里只需初始化第 1 天结束,持有股票和不持有股票时的最大收益即可。(dp[0][0]dp[0][1]

返回值

题目要求的是最大收益,最终结果存储在 dp[n-1][1]中(最后一天结束,持有股票状态无意义)

代码实现

cpp 复制代码
class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int n = prices.size();
        vector<vector<int>> dp(n, vector<int>(2, 0));
        dp[0][0] = -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] - fee);
        }
        return dp[n - 1][1];
    }
};

三、买卖股票的最佳时机 III

题目解析

给定一个数组 procesproces[i]表示第 i 天的股票价格;

约束条件

  • 不能同时参与多笔交易(已有股票就不能再买入了)
  • 最多只能进行 2 笔交易

算法思路

这道题没有交易冷冻期,交易手续费之类的;要求只能进行 2 笔交易

对于第 i 天结束,这个状态就有 6种:

进行了 0 次交易:持有股票(买入状态)不持有股票(卖出状态)

进行了 1 次交易:持有股票(买入状态)不持有股票(卖出状态)

进行了 2 次交易:持有股票(买入状态)不持有股票(卖出状态)

一次交易:股票买入 - 股票卖出

这里当股票卖出之后,认为此次交易才算完成

状态表示

  • f[i][0]:第 i 天结束,进行了 0 次交易,处于 买入 状态的最大利润
  • f[i][1]:第 i 天结束,进行了 1 次交易,处于 买入 状态的最大利润
  • f[i][2]:第 i 天结束,进行了 2 次交易,处于 买入 状态的最大利润
  • g[i][0]:第 i 天结束,进行了 0 次交易,处于 卖出 状态的最大利润
  • g[i][1]:第 i 天结束,进行了 1 次交易,处于 卖出 状态的最大利润
  • g[i][2]:第 i 天结束,进行了 2 次交易,处于 卖出 状态的最大利润

状态转移方程

  • f[i][0] = max(f[i-1][0], g[i-1][0] - prices[i])
  • f[i][1] = max(f[i-1][1], g[i-1][1] - prices[i])
  • f[i][2] = max(f[i-1][2], g[i-1][2] - prices[i])
  • g[i][0] = g[i-1][0]
  • g[i][1] = max(g[i-1][1], f[i-1][0] + prices[i])
  • g[i][2] = max(g[i-1][2], f[i-1][1] + prices[i])

初始化

这里初始化 第 1 天结束时,0 次交易,处于 买入 状态的最大收益即可(第一天结束,进行 0 笔交易,处于卖出状态)

为了不影响最终结果,第 1 天结束,进行 1、2 笔交易最大收益 都设置成 -0x3f3f3f

返回值

要求最大收益,最终结果(第 n 天结束,进行了 0、1、2次交易,处于 卖出(不持有股票)的最大收益)

max(g[n-1][0], g[n-1][1], g[n-1][2])

代码实现

cpp 复制代码
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<vector<int>> f(n, vector<int>(3, 0));
        vector<vector<int>> g(n, vector<int>(3, 0));
        f[0][0] = -prices[0];
        f[0][1] = f[0][2] = g[0][1] = g[0][2] = -0x3f3f3f;
        for (int i = 1; i < n; i++) {
            f[i][0] = max(f[i - 1][0], g[i - 1][0] - prices[i]);
            f[i][1] = max(f[i - 1][1], g[i - 1][1] - prices[i]);
            f[i][2] = max(f[i - 1][2], g[i - 1][2] - prices[i]);

            g[i][0] = g[i - 1][0];
            g[i][1] = max(g[i - 1][1], f[i - 1][0] + prices[i]);
            g[i][2] = max(g[i - 1][2], f[i - 1][1] + prices[i]);
        }
        return max(max(g[n - 1][0], g[n - 1][1]), g[n - 1][2]);
    }
};

四、买卖股票的最佳时机 IV

题目解析

给定一个数组 proces和一个整数 kproces[i]表示第 i 天的股票价格,k 表示最多的交易次数

约束条件

  • 不能同时参与多笔交易(已有股票就不能再买入了)
  • 最多只能进行 k 笔交易

算法思路

对于第 i 天结束,这个状态有 2*k 种,很显然使用有限的状态是表示不过来了;

这里就可以使用二维数组(甚至三维)来表示:

状态表示

  • f[i][j] :第 i 天结束,进行了 j 次交易,处于 买入 状态的最大利润
  • g[i][j] :第 i 天结束,进行了 j 次交易,处于 卖出 状态的最大利润

状态转移方程

  • f[i][j] = max(f[i-1][j], g[i-1][j] - prices[i])
  • g[i][j] = max(g[i-1][j], f[i-1][j-1] + prices[i])

注意 : 在填 g[i][0]时,会用到 f[i-1][-1](越界);这里需要进行特殊判断

初始化

这里初始化 第 1 天结束时,0 次交易,处于 买入 状态的最大收益(-prices[i])、第一天结束,进行 0 笔交易,处于卖出状态,最大收益为 0

为了不影响最终结果,第 i(i > 0) 天结束,进行 0~k 笔交易最大收益 都设置成 -0x3f3f3f

最终结果

求最大利润,最终结果就应该是 第 n-1 天结束,进行 0~k 笔交易,处于卖出状态的最大利润

代码实现

cpp 复制代码
class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        vector<vector<int>> f(n, vector<int>(k + 1, 0));
        vector<vector<int>> g(n, vector<int>(k + 1, 0));
        for (int i = 1; i <= k; i++)
            f[0][i] = g[0][i] = -0x3f3f3f;
        f[0][0] = -prices[0];
        g[0][0] = 0;
        for (int i = 1; i < n; i++) {
            for (int j = 0; j <= k; j++) {
                f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]);
                g[i][j] = g[i - 1][j];
                if (j > 0)
                    g[i][j] = max(g[i - 1][j], f[i - 1][j - 1] + prices[i]);
            }
        }
        int ret = -0x3f3f3f;
        for (int i = 0; i <= k; i++)
            ret = max(ret, g[n - 1][i]);
        return ret;
    }
};

本篇文章到这里就结束了,感谢支持

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws

相关推荐
北顾笙9802 小时前
day15-数据结构力扣
数据结构·算法·leetcode
AI成长日志2 小时前
【GitHub开源项目专栏】黑客松项目架构模式解析:微服务、事件驱动与Serverless实战
算法
人道领域2 小时前
【LeetCode刷题日记:24】两两交换链表
算法·leetcode·链表
北顾笙9802 小时前
day16-数据结构力扣
数据结构·算法·leetcode
AI成长日志2 小时前
【算法学习专栏】动态规划基础·简单三题精讲(70.爬楼梯、118.杨辉三角、121.买卖股票的最佳时机)
学习·算法·动态规划
wsoz2 小时前
Leetcode子串-day4
c++·算法·leetcode
汀、人工智能3 小时前
[特殊字符] 第27课:环形链表II
数据结构·算法·链表·数据库架构··环形链表ii
会编程的土豆3 小时前
【数据结构与算法】二叉树大总结
数据结构·算法·leetcode
沉鱼.443 小时前
第十届题目
算法