【算法笔记】状态机DP之买卖股票问题通用解法

【算法笔记】状态机DP之买卖股票问题通用解法

🔥个人主页大白的编程日记

🔥专栏算法笔记


前言

哈喽,各位小伙伴大家好!今天我们讲的是买卖股票问题的通用解法。话不多说,我们进入正题!向大厂冲锋!
在这里插入图片描述

一.买卖股票的最佳时机

  • 代码实现

注意这里ans初始化为0表示不交易 同时这里先包含卖入点的股票价格最小值不要紧 因为当天买入当天卖出利润为0 不影响结果!

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int ans = 0;
        int min_price = prices[0];
        for (int p : prices) {
            ans = max(ans, p - min_price);
            min_price = min(min_price, p);
        }
        return ans;
    }
};

二.买卖股票的最佳时机2

  • 代码实现

记忆化搜索:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<array<int, 2>> memo(n, {-1, -1}); // -1 表示还没有计算过
        auto dfs = [&](this auto&& dfs, int i, bool hold) -> int {
            if (i < 0) {
                return hold ? INT_MIN : 0;
            }
            int& res = memo[i][hold]; // 注意这里是引用
            if (res != -1) {
                return res; // 之前计算过
            }
            if (hold) {
                return res = max(dfs(i - 1, true), dfs(i - 1, false) - prices[i]);
            }
            return res = max(dfs(i - 1, false), dfs(i - 1, true) + prices[i]);
        };
        return dfs(n - 1, false);
    }
};

动态规划:

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

三.买卖股票的最佳时机4

记忆化搜索:

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        vector memo(n, vector<array<int, 2>>(k + 1, {-1, -1})); // -1 表示还没有计算过
        auto dfs = [&](this auto&& dfs, int i, int j, bool hold) -> int {
            if (j < 0) {
                return INT_MIN / 2; // 除 2 防止溢出
            }
            if (i < 0) {
                return hold ? INT_MIN / 2 : 0;
            }
            int& res = memo[i][j][hold]; // 注意这里是引用
            if (res != -1) { // 之前计算过
                return res;
            }
            if (hold) {
                return res = max(dfs(i - 1, j, true), dfs(i - 1, j - 1, false) - prices[i]);
            }
            return res = max(dfs(i - 1, j, false), dfs(i - 1, j, true) + prices[i]);
        };
        return dfs(n - 1, k, false);
    }
};

动态规划:

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


恰好K次

  • 递归边界判断条件增加j>0即可。
class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        vector memo(n, vector<array<int, 2>>(k + 1, {-1, -1})); // -1 表示还没有计算过
        auto dfs = [&](this auto&& dfs, int i, int j, bool hold) -> int {
            if (j < 0) {
                return INT_MIN / 2; // 除 2 防止溢出
            }
            if (i < 0) {
                return hold || j>0 ? INT_MIN / 2 : 0;
            }
            //增加j>0即可
            int& res = memo[i][j][hold]; // 注意这里是引用
            if (res != -1) { // 之前计算过
                return res;
            }
            if (hold) {
                return res = max(dfs(i - 1, j, true), dfs(i - 1, j - 1, false) - prices[i]);
            }
            return res = max(dfs(i - 1, j, false), dfs(i - 1, j, true) + prices[i]);
        };
        return dfs(n - 1, k, false);
    }
};

至少K次

  • 只需要判断天数为0时 交易次数k是否<=0即可
#include <vector>
#include <array>
#include <climits>
using namespace std;

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        if (n < 2) return 0; // 如果天数少于2天,无法进行交易

        vector<vector<array<int, 2>>> memo(n, vector<array<int, 2>>(k + 1, {0, INT_MIN}));
        auto dfs = [&](int i, int j, bool hold) -> int {
            if (j <= 0) return 0; // 如果交易次数为0或负数,无法进行交易
            if (i == -1) return INT_MIN; // 如果天数用完,还没有完成至少k次交易,返回INT_MIN
            int& res = memo[i][j][hold];
            if (res != 0 && res != INT_MIN) return res; // 之前计算过
            if (!hold) {
                res = max(dfs(i - 1, j, false), dfs(i - 1, j, true) + prices[i]);
            } else {
                res = max(dfs(i - 1, j, true), dfs(i - 1, j - 1, false) - prices[i]);
            }
            return res;
        };

        int maxProfit = INT_MIN;
        for (int j = 1; j <= k; ++j) { // 至少1次交易
            maxProfit = max(maxProfit, dfs(0, j, false));
        }
        return maxProfit;
    }
};

四.买卖股票的最佳时机3

记忆化搜索

class Solution {
public:
    int maxProfit( vector<int>& prices) {
        int n = prices.size();
        int k=2;
        vector memo(n, vector<array<int, 2>>(k + 1, {-1, -1})); // -1 表示还没有计算过
        auto dfs = [&](this auto&& dfs, int i, int j, bool hold) -> int {
            if (j < 0) {
                return INT_MIN / 2; // 除 2 防止溢出
            }
            if (i < 0) {
                return hold ? INT_MIN / 2 : 0;
            }
            int& res = memo[i][j][hold]; // 注意这里是引用
            if (res != -1) { // 之前计算过
                return res;
            }
            if (hold) {
                return res = max(dfs(i - 1, j, true), dfs(i - 1, j - 1, false) - prices[i]);
            }
            return res = max(dfs(i - 1, j, false), dfs(i - 1, j, true) + prices[i]);
        };
        return dfs(n - 1, k, false);
    }
};

动态规划

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

五.买卖股票的最佳时机含冷冻期

  • 代码实现
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<array<int, 2>> memo(n, {-1, -1}); // -1 表示还没有计算过
        auto dfs = [&](this auto&& dfs, int i, bool hold) -> int {
            if (i < 0) {
                return hold ? INT_MIN : 0;
            }
            int& res = memo[i][hold]; // 注意这里是引用
            if (res != -1) { // 之前计算过
                return res;
            }
            if (hold) {
                return res = max(dfs(i - 1, true), dfs(i - 2, false) - prices[i]);
            }
            return res = max(dfs(i - 1, false), dfs(i - 1, true) + prices[i]);
        };
        return dfs(n - 1, false);
    }
};

在这里插入图片描述

六.买卖股票的最佳时机含手续费

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int n = prices.size();
        vector<array<int, 2>> memo(n, {-1, -1}); // -1 表示还没有计算过
        auto dfs = [&](this auto&& dfs, int i, bool hold) -> int {
            if (i < 0) {
                return hold ? INT_MIN / 2 : 0; // 防止溢出
            }
            int& res = memo[i][hold]; // 注意这里是引用
            if (res != -1) {
                return res; // 之前计算过
            }
            if (hold) {
                return res = max(dfs(i - 1, true), dfs(i - 1, false) - prices[i]);
            }
            return res = max(dfs(i - 1, false), dfs(i - 1, true) + prices[i] - fee);
        };
        return dfs(n - 1, false);
    }
};

在这里插入图片描述

后言

这就是买卖股票问题的通用解法。大家自己好好消化!今天就分享到这! 感谢各位的耐心垂阅!咱们下期见!拜拜~

评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大白的编程日记.

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值