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

文章目录

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

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

【题目描述】

给定一个整数数组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++程序代码】

CPP 复制代码
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++程序代码】

CPP 复制代码
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++程序代码】

CPP 复制代码
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++程序代码】

CPP 复制代码
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;
    }
};
相关推荐
搞笑症患者4 分钟前
LeetCode Hot100 - 矩阵篇
算法·leetcode·矩阵
Peter_chq6 分钟前
【计算机网络】数据链路层
linux·c语言·开发语言·网络·c++·后端·网络协议
冠位观测者7 分钟前
【Leetcode Top 100】240. 搜索二维矩阵 II
数据结构·算法·leetcode
重生之我是数学王子30 分钟前
QT简易项目 数据库可视化界面 数据库编程SQLITE QT5.12.3环境 C++实现
数据库·c++·qt
Darkwanderor32 分钟前
迄今为止的排序算法总结
数据结构·c++·算法·排序算法
Want59540 分钟前
C/C++绘制爱心
c语言·开发语言·c++
earthzhang20211 小时前
《深入浅出HTTPS》读书笔记(12):块密码算法之迭代模式
网络协议·算法·http·https·1024程序员节
黑不溜秋的1 小时前
C++ 编程指南06 - 不要泄漏任何资源
c++
LeonNo111 小时前
ElasticSearch学习了解笔记
笔记·学习·elasticsearch
TANGLONG2222 小时前
【初阶数据结构和算法】leetcode刷题之设计循环队列
java·c语言·数据结构·c++·python·算法·leetcode