动态规划

基本思想

问题 的最优解如果可以由 子问题 的最优解推导得到,则可以先求解子问题的最优解,再构造原问题的最优解;若子问题有较多的重复出现,则可以自底向上从最终子问题向原问题逐步求解。

特点

  1. 求解一个问题的最优解
  2. 整体问题的最优解是依赖各个子问题的最优解
  3. 把大问题分解成若干个小问题,这些小问题之间还有相互重叠的更小的子问题
  4. 从上往下分析问题,从下往上求解问题

模型抽象

背包问题 为基础

m容量 n种物品 weight重量 value价值

各种问题无非就是在这几个量上做文章,这次整理这几个题就看到了 有/无 容量 的,value会变化 的等等。

例题

常规背包问题及其变体

建立 d p [ n ] [ m ] dp[n][m] dp[n][m] 的数组,保存 使用前n种物品容量为m 的情况下的最大价值

判断条件: 小于最大容量,也就是背包装得下

474.一和零

在这里插入图片描述
这道题稍微有点不一样是有两个容量,因此在建立数组的时候变成了 d p [ n ] [ m 1 ] [ m 2 ] dp[n][m_1][m_2] dp[n][m1][m2]

1402.做菜顺序

在这里插入图片描述
这个题倒是常规的背包问题,不过它这里的物品价值不是个常量,所以在算总价值的时候要用个简单的计算,其实根本思路还是一样的。

这还是个 困难 的题,前一天晚上没做这篇整理思路不太清晰,今天整理了一下其他几个题,再做这个题就很通顺的一次通过了。不过就是 执行用时内存消耗 比较大。。。目前就整理解题思路,怎么进一步优化后续再说吧

无容量的问题及其变体

建立 d p [ n ] dp[n] dp[n] 的数组,保存 使用前n种物品 的最大价值

判断条件: 符合某种条件,第一种 是背包装得下,下面例题 是比前一个数要大(上升)

300.最长上升子序列

在这里插入图片描述
这个题没有容量的问题了,变成了符合 上升序列 这个条件,但实际上跟 第一种 是一个意思,满足某种条件的时候算上当前情况就行了。

978.最长湍流子数组

在这里插入图片描述
这个题也是没有容量的,要符合 湍流子数组 的条件。相邻两个大小关系要是相反的,所以用 dp[n][2] 来存,[2] 表示该大于或者该小于。

动态规划 vs 贪心算法

面试题14-I"剪绳子"

在这里插入图片描述
这个题就很灵活,不是那种死板的背包问题,而是真的要把问题给分解成子问题,没办法去套用方法。而且这个题很烦啊 = = 长度是2或者3的时候返回的是1跟2,但用于计算更长的长度的时候,2跟3的结果就是2和3。。。。就不用拆分了。。。

另外这个题可以用贪心来做,长度大于5的时候要让3多一点,长度是4的时候要剪成两个2。

贪心我目前感觉就像是在 耍流氓 - - 我看见这个题,我要是能想出来 贪心 什么,我就知道怎么解,要是没发现,就凉了。而且贪心策略也基本上只能是提出个策略然后去证明。。(也许后续学习的过程中还会再来修正,目前就是这么理解的。。。头大 LOL

面试题14-II“剪绳子II”

在这里插入图片描述
啊这。。好像在返回的时候 $ %(1000000007)$ 就行了啊

股票类型问题

121.买卖股票的最佳时机

122.买卖股票的最佳时机II

123.买卖股票的最佳时机III

188.买卖股票的最佳时机IV

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

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

剑指Offer63.股票的最大利润

其他例题

面试题47“礼物的最大价值”

在这里插入图片描述
典型的动态规划,不过可能不是那种背包问题。
可以很容易找到 转移方程
f ( i , j ) = m a x [ f ( i , j − 1 ) , f ( i − 1 , j ) ] + g r i d ( i , j ) f(i,j)=max[f(i,j−1),f(i−1,j)]+grid(i,j) f(i,j)=max[f(i,j1),f(i1,j)]+grid(i,j)

面试题48“最长不含重复字符的子字符串”

在这里插入图片描述
动态规划嘛,就是下一个状态能由上一个状态推算出来,能 转移,就能动态规划。定义 f ( i ) f(i) f(i)表示以第 i i i个字符为结尾的 不包含重复字符的子字符串的最长长度 ,那每次算 f ( i ) f(i) f(i)的时候,我们已经知道了 f ( i − 1 ) f(i-1) f(i1),接着咋整呢?要做个分类讨论了:

  1. 如果第 i i i个字符之前没出现过,那么 f ( i ) = f ( i − 1 ) + 1 f(i) = f(i-1) + 1 f(i)=f(i1)+1
  2. 如果第 i i i个字符之前出现过了,那么就复杂点了。计算一下第 i i i个字符和它 上次 出现的位置的距离,记为 d d d
    1. d ≤ f ( i − 1 ) d \leq f(i-1) df(i1)。说明第 i i i个字符出现在了 f ( i − 1 ) f(i-1) f(i1)里面了。但!第 i i i个字符的两次相邻出现之间就没有其他重复字符了,所以 f ( i ) = d f(i) = d f(i)=d
    2. d > f ( i − 1 ) d > f(i-1) d>f(i1)。说明第 i i i个字符上一次出现已经是在 f ( i − 1 ) f(i-1) f(i1)外面了,所以 f ( i ) = f ( i − 1 ) + 1 f(i) = f(i-1) + 1 f(i)=f(i1)+1

在上述过程中,需要有个辅助的数组来记录一下各个字符上一次出现的位置 i n d e x index index,然后就可以实现算法了。

面试题42“连续子数组的最大和”

在这里插入图片描述
这个题有个类似于找规律的解法,直接上代码看吧。重点不是这个:

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        for i in range(1, len(nums)):
            nums[i] += max(nums[i - 1], 0)
        return max(nums)

意思是说,前面的最大连续和 如果是正的我就可以要它,不然还不如就我自己,不能要拖后腿的。

接下来是进阶:
动态规划 的解决法
在这里插入图片描述

面试题49“丑数”

在这里插入图片描述
这题看着是真复杂。。。基本思路还是一样的,但是考虑的因素多了,感觉就有点转不过弯了 QAQ
先逆向思维一下,某个数是第 n n n个丑数,它无非就是由之前的某个丑数乘上2或者3或者5。之前这个数就有三个可能 x a x_a xa x b x_b xb x c x_c xc。所以我们只要判断 x a × 2 x_a \times 2 xa×2 x b × 3 x_b \times 3 xb×3 x c × 5 x_c \times 5 xc×5哪个更小,就是这个丑数了。计算过程中要维护这三个变量,并且每当某个变量“中奖了”就给它 +1 就行了。
在这里插入图片描述
代码也附上:

class Solution:
    def nthUglyNumber(self, n: int) -> int:
        dp, a, b, c = [1] * n, 0, 0, 0
        for i in range(1, n):
            n2, n3, n5 = dp[a] * 2, dp[b] * 3, dp[c] * 5
            dp[i] = min(n2, n3, n5)
            if dp[i] == n2: a += 1
            if dp[i] == n3: b += 1
            if dp[i] == n5: c += 1
        return dp[-1]

面试题60“n个骰子的点数”

在这里插入图片描述

  1. 基于递归求骰子点数,时间效率不够高
  2. 基于循环求骰子点数,时间性能好
  3. 动态规划
    d p [ i ] [ j ] dp[i][j] dp[i][j] 表示使用前i个骰子,和为j的条件下共有多少个情况
    考虑到转移的问题,第i个骰子和为j的时候,也就是i-1个骰子和分别为j-1到j-6的六种情况数量之和,代码如下:
for k in range(6):
	dp[i][j] += dp[i-1][j-k]

面试题63“股票的最大利润”

在这里插入图片描述
动态规划可以解决,其实最主要的问题是要保留第i-1个数字以前的最小值,才能知道第i个数字的时候的最大利润。

参考链接

1. https://www.cnblogs.com/hithongming/p/9229871.html
2. https://blog.csdn.net/weixin_40984271/article/details/82560731

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值