动态规划- 股票问题专题(详解+多例子+源码)

动态规划做题思路

像这种股票问题,我们首先想多状态动态规划 ,先分析

① 状态表示: dp[ i ]表示什么

我们这里用的是多状态,所以不用dp命名,用f[ i ]和 g[ i ]命名

一般我们 用 f[ i ]表示第 i 天当前手中持有股票最大收益

               用g[ i ]表示第 i 天当前手中未持有股票最大收益

当然如果有其他的情况要分析是否需要其他状态 需要具体分析

②状态转移方程

因为我们用的是多状态,所以我们要分析没个状态是否可以由其他状态得到 怎么得到的,或者由自己得到,一定要分析全面 

状态转移要根据状态表示来推导,我们状态表示是最大收益,就要将可以转移到该状态的所有情况取最大值,填入到当前值中 如果有其他状态表示 根据具体分析即可

买股票的最佳时机含冷冻期 (medium)

首先先解释题目:我们可以进行多次交易,但是当我们卖出股票的时候,我们会被冷冻一天,这一天中不能进行买股票行为

 

 像这种买股票的问题,我们可以用多状态动态规划来解决(直接秒杀)需要分析

如上图 :因为含冷冻期,我们把冷冻期的状态也加上,具体分析画出状态积 

F可以从未持有股票进行一次购买转移得到,也可以由自己无需操作得到

G可以从冷冻期第二天解冻得到,也可以有自己无需操作的到

T只可以从上一天卖出股票转换得到

分析出了每种状态的转移方程,只需要对每种状态取最大值即可

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];
        dp[0][1]=dp[0][2]=0;
        //dp[i][0]当前状态是买入
        //dp[i][1]当前状态是可交易
        //dp[i][2]当前状态被冻结

        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(max(dp[n-1][0],dp[n-1][1]),dp[n-1][2]);
    }
};

 

 买股票的最佳时机含手续费(medium)

解释题目:这次不含冷冻期,但是每次交易需要付手续费,还是可以多次交易 。首先我们要明确一次交易,我们可以在买入的时候付手续费,卖出的时候就不需要再付,或者买入的时候不付,卖出的时候付手续费,这里我们都以卖出的时候付手续费

还是先分状态,这题只需要分两种状态,直接画出状态积,分析每种状态如何转化:

F(持有)可以由上一天的持有状态,什么都不做转化来,也可以上天手中未持有股票,通过买入操作转化来

G(未持有)可以由上一天未持有 什么都不做转化来,还可以上一天手中持有股票,进行卖出操作(同时需要付手续费)转化来

所以状态转移方程就的到了

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

 买股票的最佳时机Ⅲ(hard)

这道题没有手续费,没有冷冻期,但是只可以进行两次交易,我们还是需要明确一次交易的概念,只可以进行两次 

 

这次我们需要定义五个状态,每个状态表示如图,我们只需要模拟这两次购买 即可的到状态积,很容易分析出状态转移方程,其中G0一直都是0 因为我们一直没有进行购买操作,手中什么都没有。 

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        vector<int>G0(n);
        vector<int>G1(n);
        vector<int>G2(n);
        vector<int>F1(n);
        vector<int>F2(n);
        F1[0]=-prices[0];
        F2[0]=-prices[0];
        //从第二个元素开始填
        
        for(int i=1;i<n;i++)
        {
            F1[i]=max(G0[i-1]-prices[i],F1[i-1]);
            G1[i]=max(F1[i-1]+prices[i],G1[i-1]);
            F2[i]=max(G1[i-1]-prices[i],F2[i-1]);
            G2[i]=max(F2[i-1]+prices[i],G2[i-1]);
        }
        return G2[n-1];
    }
};

 买股票的最佳时机Ⅳ(hard)

类比上一道题:

在我们上一道题填表的时候 :

可以看到十分的有规律,上道题只能交易两次,但是这道题只能交易k次,所以两道题几乎一样

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        const int INF = 0x3f3f3f3f;
        // 处理⼀个细节问题
        int n = prices.size();
        k = min(k, n / 2);
        // 创建 dp 表
        // 初始化
        // 填表
        // 返回值
        vector<vector<int>> f(n, vector<int>(k + 1, -INF));
        auto g = f;
        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 >= 1) // 如果状态存在
                    g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]);
            }
        }
        int ret = 0;
        for (int j = 0; j <= k; j++)
            ret = max(ret, g[n - 1][j]);
        return ret;
    }
};

 

  • 31
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会尽力为您解答。首先,让我们来了解一下凑硬币问题。 在凑硬币问题中,我们需要找到最少的硬币数来凑成给定的金额。例如,如果我们需要凑成 11 元,而有 1 元、2 元、5 元三种不同面值的硬币可供使用,那么最少需要使用 3 枚硬币,即 5 元、5 元和 1 元。 下面是一种使用动态规划解决凑硬币问题的方法: 1. 创建一个长度为 amount+1 的数组 dp,其中 dp[i] 表示凑成面值为 i 元所需的最少硬币数。 2. 初始化 dp[0] 为 0,其余元素初始化为一个较大的数(例如 amount+1),表示当前面值无法被凑成。 3. 遍历硬币数组 coins,对于每个硬币,遍历 dp 数组,更新 dp[j] 的值为 dp[j-coins[i]]+1,其中 j>=coins[i],表示当前面值可以被凑成。 4. 最终 dp[amount] 就是凑成 amount 元所需的最少硬币数,如果 dp[amount]>amount,则表示无法凑成给定金额。 下面是使用 JavaScript 实现该算法的代码: ```javascript function coinChange(coins, amount) { const dp = new Array(amount + 1).fill(amount + 1); dp[0] = 0; for (let i = 0; i < coins.length; i++) { for (let j = coins[i]; j <= amount; j++) { dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1); } } return dp[amount] > amount ? -1 : dp[amount]; } ``` 这个函数接受两个参数,一个是硬币数组 coins,另一个是需要凑成的金额 amount。最终返回凑成 amount 元所需的最少硬币数,如果无法凑成则返回 -1。 例如,若 coins = [1, 2, 5],amount = 11,则 coinChange(coins, amount) 的返回值为 3,表示凑成 11 元最少需要 3 枚硬币。 希望这个简单的解释和代码能够帮到您,如果您还有其他问题,请随时问我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值