0、什么样的题适合使用动态规划
如果某一问题有很多 重叠子问题,使用动态规划是最有效的
❓计算f(N)的时候需要f(N-1),也就是需要f(N-2),等等等,如果我们直接计算第n个的值,会导致前面的值被重复计算很多很多次
⭐️把前面的值记录下来
1、刷过的习题思路汇总
1️⃣509. 斐波那契数
- 重叠子问题:计算第n个值需要第n-1和n-2的值
- dp[i] 代表第i个数字的值
- dp[0] = 0 dp[1] = 1
- d p [ i ] = d p [ i − 1 ] + d p [ i − 2 ] dp[i] = dp[i-1] + dp[i-2] dp[i]=dp[i−1]+dp[i−2]
2️⃣70. 爬楼梯
- 重叠子问题:爬到第n阶台阶的方法就是爬到第n-1阶台阶的方法(一步上来)➕ 爬到第n-2阶台阶的方法(两步上来)
- 和斐波那契数一模一样
3️⃣746. 使用最小花费爬楼梯
- 重叠子问题:爬到第n阶台阶的最少花费就是爬到第n-1阶台阶的最少花费加第n-1个台阶的花费(一步上来)和 爬到第n-2阶台阶的最少花费加第n-2个台阶的花费(两步上来)两者的最小值
- dp[i]:到达第i阶台阶需要的最小花费
- dp[0] = 0 dp[1] = 0
- d p [ i ] = m i n ( d p [ i − 1 ] + c o s t [ i − 1 ] , d p [ i − 2 ] + c o s t [ i − 2 ] ) dp[i] = min(dp[i-1] + cost[i-1] , dp[i-2] + cost[i-2]) dp[i]=min(dp[i−1]+cost[i−1],dp[i−2]+cost[i−2])
4️⃣62. 不同路径
- 重叠子问题:到(i,j)的路径要么从(i-1,j)要么从(i,j-1)两条路来,也就是说求到(i,j)的方法数就是到(i-1,j)的方法数 ➕ 到(i,j-1)的方法数
- dp[i][j]表示从(0,0)到(i,j)有多少路径
- 第一行和第一列都是1(只有一种方法)
- d p [ i ] [ j ] = d p [ i ] [ j − 1 ] + d p [ i − 1 ] [ j ] dp[i][j] = dp[i][j-1] + dp[i-1][j] dp[i][j]=dp[i][j−1]+dp[i−1][j]
5️⃣63. 不同路径 II(障碍物版本)
- 初始化有点变化,当第一行或者第一列中出现障碍物,后面的值为0
- 只有当(i,j)不是障碍物才会有值,否则就是0,代表到这个点的路径置零
6️⃣ 343. 整数拆分
- 重叠子问题:求n的拆分乘积最大值?最简单的就是拆分成两个数 i i i 和 ( n − i ) (n-i) (n−i) 的乘积最大值,但是由于可以拆成更多的数,所以结果应该是拆分项 i i i 和 ( n − i ) (n-i) (n−i) 的拆分乘积最大值,这里其实就说明我们需要更小的整数拆分结果来得到n的整数拆分结果
- dp[i]:分拆数字i,可以得到的最大乘积为dp[i]。
- dp[0] = 0 dp[1] = 0 dp[2] = 1
- d p [ i ] = m a x ( d p [ i ] , ( i − j ) ∗ j , d p [ i − j ] ∗ j ) dp[i] = max({dp[i], (i - j) * j, dp[i - j] * j}) dp[i]=max(dp[i],(i−j)∗j,dp[i−j]∗j)
7️⃣96. 不同的二叉搜索树
- 背景了解 :二叉搜索树就是做变节点小于根节点,右边节点大于根节点。如果节点数为1,那么只有一种搜索树;两个节点,就有两种;三个节点,就是五种。在统计三个节点的时候发现,如果左\右子树是两个节点就是2种,一个节点就是1种
- 重叠子问题:求n哥节点的二叉搜索树其实就是求左子树的二叉搜索树的种类数 加上 右子树的二叉搜索树的种类数,左字数的节点数量可以从1一直到n-1,一直累加就好了
- dp[i]:节点为i的二叉树搜索树的种类数
- dp[1] = 1 dp[2] = 2
- d p [ i ] = d p [ i ] + d p [ j − 1 ] ∗ d p [ i − j ] dp[i] = dp[i] + dp[j-1] * dp[i-j] dp[i]=dp[i]+dp[j−1]∗dp[i−j]
8️⃣ 01背包问题
- 问题描述 :有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大
- 重叠子问题:每一个物品,有两种选择:“放或者不放 ”
- 如果放物品i,背包容量就变成w-weight[i],那么此时背包容量w的最大价值为value[i] + 背包容量为(w-weight[i])的最大价值(子问题)
- 如果不放物品i,背包容量还是w,那么此时背包容量w的最大价值为背包容量为(w)的最大价值
- 二维dp数组
- dp[i][j]代表背包容量为j的情况下,从下标为[0, i]的物品里面任意取,能达到的最大价值
- dp[0][x] = 0
- d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − w e i g h t [ i ] ] + v a l u e [ i ] ) dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]) dp[i][j]=max(dp[i−1][j],dp[i−1][j−weight[i]]+value[i])
- 遍历顺序:物品:正序;背包容量:正序
- 一维dp数组
- dp[j]代表背包容量为j装的最大价值
- dp[0] = 0
- d p [ j ] = m a x ( d p [ j ] , d p [ j − n u m s [ i ] ] + n u m s [ i ] ) dp[j] = max(dp[j],dp[j-nums[i]]+nums[i]) dp[j]=max(dp[j],dp[j−nums[i]]+nums[i])
- 遍历顺序:物品:正序[0,物品数量-1];背包容量:倒序[背包容量,物品i的重量,-1]
9️⃣ 416. 分割等和子集
- 题目:判断是否可以将正整数数组分割成两个子集,使得两个子集的元素和相等。
- 思路 :
- 数组中的每个元素就是物体, 价值和重量都输数值
- 背包容量就是sum/2(不能整除就说明不能分割)
- 判断是否可以选择一些物品(不重复),总和等于背包容量
- dp[背包容量]代表背包容量下的背包能够放入的最大数值和,如果这个值等于sum/2就说明可以分割。
- dp[j]代表背包容量为j装的最大数值和(价值)
- d p [ j ] = m a x ( d p [ j ] , d p [ j − n u m s [ i ] ] + n u m s [ i ] ) dp[j] = max(dp[j],dp[j-nums[i]]+nums[i]) dp[j]=max(dp[j],dp[j−nums[i]]+nums[i])
- 遍历顺序:数组:正序[0,数组长度-1];背包容量:倒序[背包容量,数值i,-1]
就相当于要凑一个背包容量为sum/2的背包。带入01问题就是,使用数值装进背包,
1️⃣0️⃣1049. 最后一块石头的重量 II
- 题目:粉碎石头,使得剩下的重量最小
- 思路 : 把石头分成两组重量相似的两组石头,这样剩下的石头重量最小
- 物体就是石头,重量和价值都是石头重量
- 背包容量就是sum/2
- 选择一些石头(不重复)在背包容量范围内,放入石头的重量最大值(价值)
- 基本上和分割等和子集一样但是,不是判断,而是两个子集相减
- dp[j]:背包容量为j,能够放进去的最大石头重量(价值)
- d p [ j ] = m a x ( d p [ j ] , d p [ j − s t o n e s [ i ] ] + s t o n e s [ i ] ) dp[j] = max(dp[j],dp[j-stones[i]]+stones[i]) dp[j]=max(dp[j],dp[j−stones[i]]+stones[i])
- 遍历顺序:石头:正序[0,石头数量-1];背包容量:倒序[背包容量,石头i重量,-1]
1️⃣1️⃣ 494. 目标和
-
题目:正数数组通过不同元素前面加法或者减法,得到目标值的方法有多少种
-
思路 : 通过公式推导,这个问题就是找到一些数的的和为(target+sum)/2,这样的方法有多少种
- 物体就是数值,重量就是数值大小,因为问题不再是求装满物品的价值最大,而是组合最多,所以物品不需要价值属性了
- 背包容量就是(sum+target)/2
-
dp[j]:填满j(包括j)这么大容积的包,有dp[j]种方法(注意不是最大价值了 )
-
d p [ j ] = d p [ j ] + d p [ j − n u m s [ i ] ] dp[j] = dp[j] + dp[j - nums[i]] dp[j]=dp[j]+dp[j−nums[i]]
- 对于物品i,有两种情况,装或者不装,装的话的方法数就变成了dp[j-nums[i]],不装就还是dp[j]
-
遍历顺序:物品:正序[0,物品数量-1];背包容量:倒序[背包容量,数值大小,-1]
1️⃣2️⃣ 474. 一和零
- 题目:给你一个二进制字符串数组 strs 和两个整数 m 和 n 。请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。
- 思路 : 相当于每个字符串有两个质量(0的数量和1的数量),背包也有两个容量
- 物品 :每个字符串,重量为两个值分别是0的数量和1的数量,价值就是1(代表能装进去)
- 背包容量 :两个,0的容量为m,1的容量为n
- dp[i][j]代表0的容量为i,1的容量为j的背包能装的字符串的个数
- d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − n u m 0 ] [ j − n u m 1 ] + 1 ) dp[i][j] = max(dp[i][j],dp[i-num_0][j-num_1]+1) dp[i][j]=max(dp[i][j],dp[i−num0][j−num1]+1)
- 遍历顺序:字符串:正序[0,字符串数量-1];背包容量(2个):倒序[背包容量,字符串的质量,-1]