【动态规划】 买卖股票的最佳时机IV(hard)(每日一题)

  🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇

                                      ⭐动态规划⭐

🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇🎇


前言

动态规划(Dynamic Programming, DP)是一种在计算机科学和数学中用于解决最优化问题的方法。它特别适用于可以分解为互相重叠的子问题的问题,并且这些子问题的解可以被存储起来以避免重复计算,从而提高效率。

动态规划的基本思想

  1. 最优子结构:问题的最优解包含了其子问题的最优解。
  2. 重叠子问题:求解过程中,子问题被重复计算多次。

动态规划的步骤

  1. 确定状态:定义问题的状态表示,即用一组变量来描述问题的关键特征。
  2. 状态转移方程:找到如何从一个状态转移到另一个状态的规则或公式。
  3. 边界条件:定义初始状态或基本情况,这是递归终止的条件。
  4. 计算顺序:确定解决问题的顺序,通常是自底向上或自顶向下

 实现方式

动态规划可以通过两种主要的方式来实现:

  • 自顶向下(递归 + 记忆化):从问题的大状态开始,逐步递归到小状态,使用记忆表来存储中间结果。
  • 自底向上(迭代):从最小的状态开始,逐步构建更大的状态直到达到最终的目标状态。

示例

假设我们要解决斐波那契数列问题第 n 个斐波那契数 F(n) = F(n-1) + F(n-2),F(0) = 0, F(1) = 1。

自顶向下方法:

1def fibonacci(n, memo={}):
2    if n in memo:
3        return memo[n]
4    if n <= 1:
5        return n
6    memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo)
7    return memo[n]

自底向上方法:

1def fibonacci(n):
2    if n <= 1:
3        return n
4    prev, curr = 0, 1
5    for i in range(2, n+1):
6        prev, curr = curr, prev + curr
7    return curr

 回溯算法是⼀种⾮常重要的算法,可以解决许多组合问题、排列问题和搜索问题等。回溯算法的核⼼ 思想是搜索状态树,通过遍历状态树来实现对所有可能解的搜索。回溯算法的模板⾮常简单,但是实 现起来需要注意⼀些细节,⽐如如何做出选择、如何撤销选择等。


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

题目链接:188. 买卖股票的最佳时机 IV - 力扣(LeetCode)

算法思路:

问题描述

给定一个整数数组 prices,其中 prices[i] 是第 i 天的股票价格,以及一个整数 k 表示允许的最大交易次数,目标是计算最多进行 k 次交易可以获得的最大利润。

状态表示

定义两个二维数组 fg,其中:

  • f[i][j] 表示第 i 天结束后,已完成 j 次交易且处于“买入”状态时的最大利润。
  • g[i][j] 表示第 i 天结束后,已完成 j 次交易且处于“卖出”状态时的最大利润。

状态转移方程

对于每个状态 f[i][j]g[i][j],我们需要考虑如何从前一天的状态转移过来。

对于 f[i][j]
  • f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i])
    • f[i - 1][j] 表示第 i 天什么也不做,保持买入状态。
    • g[i - 1][j] - prices[i] 表示第 i 天买入股票。
对于 g[i][j]
  • g[i][j] = max(g[i - 1][j], f[i - 1][j - 1] + prices[i]),其中 j >= 1
    • g[i - 1][j] 表示第 i 天什么也不做,保持卖出状态。
    • f[i - 1][j - 1] + prices[i] 表示第 i 天卖出股票,完成第 j 次交易。

初始化

  • 当 i = 0 时,只能处于“买入”状态,即 f[0][0] = -prices[0]
  • 其他所有不存在的状态初始化为足够小的值 -INF (例如 -0x3f3f3f3f),以确保不会干扰后续的最大值计算。

填表顺序

  • 按照日期顺序从前往后填充表格。
  • 对于每一天,先填充 f[i] 再填充 g[i],且从交易次数 j = 1 开始逐次增加。

返回值

  • 返回 g 表最后一行的最大值作为答案。

 代码实现:

class Solution {
    public int maxProfit(int k, int[] prices) {
       //这题和III的变化就是交易次数变成参数了 但其实还是一样的
       int INT = 0x3f3f3f3f;
       //建dp表
       //初始化
       //填表
       //返回值
       int n = prices.length;
       k+=1;
       int[][] f = new int[n][k];
       int[][] g = new int[n][k];
       
       for(int j =0;j<k;j++){
          f[0][j] = -INT;
          g[0][j] = -INT;
       }
       g[0][0] = 0;
       f[0][0] = -prices[0];

       for(int i = 1;i<n;i++){
        for(int j = 0;j<k;j++){
            f[i][j] = Math.max(f[i-1][j],g[i-1][j]-prices[i]);
            g[i][j] = g[i-1][j];
            if(j-1>=0){
                g[i][j] = Math.max(g[i][j],f[i-1][j-1]+prices[i]);
            }
        }
       }
       int ret =0;
       for(int i =0;i<k;i++){
          ret = Math.max(ret,g[n-1][i]);
       }
       return ret;
    }
}


总结

这道题和上道题一样的,只是交易次数变了.其实还是一样的

动动手点个赞会让作者更开心,感谢阅览,加油哇各位 !

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值