蓝桥杯刷题_day9_动态规划_简单多状态

文章介绍了如何运用动态规划解决股票买卖问题,涉及考虑持有股票和不持有股票的状态,以及在有限交易次数和冷冻期的约束下计算最大利润。给出了四个相关问题的详细解题思路和C++代码示例。
摘要由CSDN通过智能技术生成

对于这类股票买卖问题,动态规划是一个常见且有效的解法。动态规划的核心在于找到状态表示和状态转移方程。对于股票买卖问题,通常有两个状态需要考虑:当前持有股票和当前不持有股票。针对特定的问题,可能还需要增加额外的状态来满足题目的限制条件,例如冷冻期和最大交易次数。

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

【题目描述】
给定一个整数数组prices,其中第 prices[i] 表示第 _i_ 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

  • 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

【输入样例】

prices = [1,2,3,0,2]

【输出样例】

3

【数据规模与约定】

  • 1 <= prices.length <= 5000
  • 0 <= prices[i] <= 1000

【解题思路】

  1. 状态表示
  • dp[i][0] 表示第 i 天结束时持有股票所得最多现金,dp[i][1] 表示第 i 天结束时不持有股票且处于冷冻期所得最多现金,dp[i][2] 表示第 i 天结束时不持有股票且不处于冷冻期所得最多现金。
  1. 状态转移方程:
  • dp[i][0] 可以从前一天持有股票 dp[i-1][0] 转移过来,或者前一天不持有股票且不处于冷冻期 dp[i-1][2] 转移过来(即今天买入)。
  • dp[i][1] 只能从前一天持有股票转移过来(即今天卖出),因此 dp[i][1] = dp[i-1][0] + prices[i]
  • dp[i][2] 可以从前一天不持有股票且不处于冷冻期 dp[i-1][2] 转移过来,或者前一天不持有股票且处于冷冻期 dp[i-1][1] 转移过来。
  1. 初始状态:
  • dp[0][0] = -prices[0],第一天买入股票。
  • dp[0][1]dp[0][2] 都是 0,因为第一天结束时不可能处于冷冻期或有利润。
  1. 最终答案:
    最后一天不持有股票的情况下利润最大,所以返回 max(dp[n-1][1], dp[n-1][2])

【C++程序代码】

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();

        vector<vector<int> > dp(n,vector<int>(3));
        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][2]);
            dp[i][2] = dp[i - 1][0] + prices[i];
        }

        return max(dp[n - 1][1], dp[n - 1][2]);
    }
};

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

【题目描述】
给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

**注意:**这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

【输入样例】

prices = [1, 3, 2, 8, 4, 9], fee = 2

【输出样例】

8

【数据规模与约定】

  • 1 <= prices.length <= 5 * 104
  • 1 <= prices[i] < 5 * 104
  • 0 <= fee < 5 * 104

【解题思路】
dp[i][0] 表示第 i 天结束时持有股票所得最多现金,dp[i][1] 表示第 i 天结束时不持有股票所得最多现金。

【C++程序代码】

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int n = prices.size();

        vector<vector<int> > dp(n, vector<int>(2));
        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][0] - fee + prices[i], dp[i - 1][1]);
        }

        return dp[n - 1][1];
    }
};

买卖股票的最佳时机III

【题目描述】
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

【输入样例】

prices = [3,3,5,0,0,3,1,4]

【输出样例】

6

【数据规模与约定】

  • 1 <= prices.length <= 105
  • 0 <= prices[i] <= 105

【解题思路】
对于每一天,我们考虑两种状态:

  • 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])表示今天我不持有股票,可能是昨天就不持有的,也可能是今天卖出的。

考虑初始状态,第 0 天结束时:

  • f[0][0] = -prices[0]表示第 0 天买入股票。
  • g[0][0] = 0 表示第 0 天不操作。

由于最多进行两次交易,所以 j 的范围是 0 到 2。

最后的答案就是在最后一天,不持有股票且进行了 0、1、2 次交易中的最大值,即 max({ g[n - 1][0], g[n - 1][1], g[n - 1][2] })

【C++程序代码】

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        int k = 2;
        vector<vector<int> > f(n, vector<int>(k + 1));
        vector<vector<int> > g(n, vector<int>(k + 1));

        f[0][0] = -prices[0];
        g[0][0] = 0;
        for (int i = 1; i < k + 1; i++)
        {
            f[0][i] = -0x3f3f3f3f;
            g[0][i] = -0x3f3f3f3f;
        }

        for (int i = 1; i < n; i++)
        {
            for (int j = 0; j < k + 1; 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][j], f[i - 1][j - 1] + prices[i]);
            }
        }
        return max({ g[n - 1][0],g[n - 1][1],g[n - 1][2] });
    }
};

买卖股票的最佳时机IV

【题目描述】
给你一个整数数组 prices 和一个整数 k ,其中 prices[i] 是某支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 k 次。

**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

【输入样例】

k = 2, prices = [3,2,6,5,0,3]

【输出样例】

7

【数据规模与约定】

  • 1 <= k <= 100
  • 1 <= prices.length <= 1000
  • 0 <= prices[i] <= 1000

【解题思路】
这个问题是前一个问题的泛化,解题思路是一样的,只是这里的 j 是从 0 到 k。

  • f[i][j] 表示第 i 天结束时,已经完成了 j 笔交易,并且手上持有一只股票的最大利润。
  • g[i][j] 表示第 i 天结束时,已经完成了 j 笔交易,并且手上不持有股票的最大利润。

状态转移方程与上面一样。初始状态也类似,只是需要初始化所有可能的交易次数。

最后的答案是在最后一天,不持有股票且进行了 0 到 k 次交易中的最大值,即通过遍历 g[n - 1][j] 来找到最大值。

【C++程序代码】

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        vector<vector<int> > f(n, vector<int>(k + 1));
        vector<vector<int> > g(n, vector<int>(k + 1));

        f[0][0] = -prices[0];
        g[0][0] = 0;
        for (int i = 1; i < k + 1; i++)
        {
            f[0][i] = -0x3f3f3f3f;
            g[0][i] = -0x3f3f3f3f;
        }

        for (int i = 1; i < n; i++)
        {
            for (int j = 0; j < k + 1; 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][j], f[i - 1][j - 1] + prices[i]);
            }
        }
        int num_max = 0;
        for (int i = 0; i < k + 1; i++)
        {
            num_max = max(g[n - 1][i], num_max);
        }
        return num_max;
    }
};
  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

排骨炖粉条

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

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

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

打赏作者

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

抵扣说明:

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

余额充值