动态规划Dynamic programming笔记
- 笔记用途
- 介绍及解题思路
- 题目类型分类
-
- 坐标型动态规划
- 序列型动态规划
-
- Paint House
- 涂房子2(Paint House2)
- 最长连续单调子序列
- Decode Ways
- 比特位技术(Counting Bits)
- 打家劫舍(House Robber)
- 打家劫舍 II(House Robber II)
- 买卖股票的最佳时机(Best Time To Buy And Sell Stock)
- 买卖股票的最佳时机 III(Best Time To Buy And Sell Stock III)建议回看视频
- 买卖股票的最佳时机 IV(Best Time To Buy And Sell Stock IV)5阶段拓展到2k+1
- 最长上升子序列(Longest Increasing Subsequence)
- 俄罗斯套娃信封问题(Russian Doll Envelopes)
- 双序列型动态规划(特殊,属于序列型的拓展)
- 划分型动态规划
- 区间型动态规划
- 背包型动态规划
- 博弈型动态规划
笔记用途
写这篇文章的目的主要是想在接下来的解题过程中能够通过这篇文章快速的回想起当初解题的思路,如果没有看过视频建议先去观看一下动态规划专题讲解,视频放在了代码中,相关题目也可以在leetcode中找到,如果对于你有帮助,给我点个赞吧!
链接:https://pan.baidu.com/s/1DAnUbgWvdS5o8_WeAFzdlA
提取码:7b3d
复制这段内容后打开百度网盘手机App,操作更方便哦
介绍及解题思路
动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。能采用动态规划求解的问题的一般要具有3个性质:
(1) 最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
(2) 无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
(3) 有重叠子问题即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)
动态规划解题思路(参考B站九章算法):
(1) 确定状态:分为最后一步以及子问题
(2) 写状态转移方程
(3) 规定初始条件以及边界情况
(4) 计算顺序(消除冗余优化空间)
例题:CoinChange
题目链接: link.
假设你有三种硬币,分别面值2元,5元和7元,每种硬币都有足够多 , 买一本书需要27元 ,如何用最少的硬币组合正好付清,且不需要对方找钱。
(1) 确定状态:状态在动态规划中的作用属于定海神针 。简单的说,解动态规划的时候需要开一个数组,数组的每个元素f[i]或者f[i][j]代表什么– 类似于解数学题中,X,Y,Z代表什么。
最后一步是最优策略肯定是K枚硬币a1, a2,…, aK 面值加起来是27所以一定有一枚最后的硬币: aK • 除掉这枚硬币,前面硬币的面值加起来是27- aK。
子问题最少用多少枚硬币可以拼出27- aK。
(2) 写状态转移方程
最后那枚硬币aK只可能是2,5或者7 ;如果aK是2,f(27)应该是f(27-2) + 1 (加上最后这一枚硬币2);如果aK是5,f(27)应该是f(27-5) + 1 (加上最后这一枚硬币5) ;如果aK是7,f(27)应该是f(27-7) + 1 (加上最后这一枚硬币7)。
所以状态转移方程为:
(3) 规定初始条件以及边界情况
初始条件:f[0] = 0
边界情况:X-2, X-5 或者X-7小于0时无法拼出定义为正无穷。
(4) 计算顺序(消除冗余优化空间)
初始条件:f[0] = 0
• 然后计算f[1], f[2], …, f[27]
• 当我们计算到f[X]时,f[X-2], f[X-5], f[X-7]都已经得到结果了
public int coinChange(int[] coins, int amount){
int [] dp = new int[amount+1];
int n = coins.length;
//初始化
dp[0] = 0;
//状态转移方程
for (int i = 1;i <= amount;i++){
dp[i] = Integer.MAX_VALUE;
for (int j = 0;j < n;j++){
//考虑边界情况
if (i>=coins[j] && dp[i-coins[j]] != Integer.MAX_VALUE ){
dp[i] = Math.min(dp[i],dp[i-coins[j]]+1);
}
}
}
//表示无法拼出返回-1
if (dp[amount]==Integer.MAX_VALUE){
return -1;
}
return dp[amount];
}
题目类型分类
动态规划类题目主要分类为:
– 坐标型动态规划 (20%)重要
– 序列型动态规划 (20%)重要
– 划分型动态规划 (20%)重要
– 区间型动态规划 (15%)重要
– 背包型动态规划 (10%)重要
– 拓扑型动态规划 (5%)
– 博弈型动态规划 (5%)
– 综合性动态规划 (5%)
坐标型动态规划
• 最简单的动态规划类型
• 给定一个序列或网格
• 需要找到序列中某个/些子序列或网格中的某条路径 – 某种性质最大/最小 – 计数 – 存在性
• 动态规划方程f[i]中的下标i表示以ai为结尾的满足条件的子序列的性质,f[i][j] 中的下标i, j表示以格子(i, j)为结尾的满足条件的路径的性质
– 最大值/最小值 – 个数
– 是否存在
• 坐标型动态规划的初始条件f[0]就是指以a0为结尾的子序列的性质
Unique Paths
题目链接: link.
有一个机器人的位于一个 m × n 个网格左上角。机器人每一时刻只能向下或者向右移动一步。机器人试图达到网格的右下角。问有多少条不同的路径?
(1) 确定状态:分为最后一步以及子问题
最后一步:无论机器人用何种方式到达右下角,总有最后挪动的一步:向右 或者 向下右下角坐标设为(m-1, n-1),那么前一步机器人一定是在(m-2, n-1)或者(m-1, n-2)
子问题为:机器人有多少种方式从左上角走到(m-2, n-1)和(m-1, n-2)
(2) 写状态转移方程
(3) 规定初始条件以及边界情况
初始条件:f[0][0] = 1,因为机器人只有一种方式到左上角(什么都不做)
边界情况:i = 0 或 j = 0,则前一步只能有一个方向过来及f[i][j]=1(i0或者 j0)
(4) 计算顺序(消除冗余优化空间)
• f[0][0] = 1
• 计算第0行:f[0][0], f[0][1], …, f[0][n-1]
• 计算第1行:f[1][0], f[1][1], …, f[1][n-1]
• …• 计算第m-1行:f[m-1][0], f[m-1][1], …, f[m-1][n-1]
Unique Paths II
题目链接: link.
• 给定m行n列的网格,有一个机器人从左上角(0,0)出发,每一步可以向下 或者向右走一步
• 网格中有些地方有障碍,机器人不能通过障碍格
• 问有多少种不同的方式走到右下角
(1) 确定状态:分为最后一步以及子问题
最后一步:无论机器人用何种方式到达右下角,总有最后挪动的一步:向右 或者 向下右下角坐标设为(m-1, n-1),那么前一步机器人一定是在(m-2, n-1)或者(m-1, n-2)
子问题为:机器人有多少种方式从左上角走到(m-2, n-1)和(m-1, n-2)
(2) 写状态转移方程
(3) 规定初始条件以及边界情况
初始条件:f[0][0] = 1,因为机器人只有一种方式到左上角(什么都不做)
边界情况:
(4) 计算顺序(消除冗余优化空间)
• f[0][0] = 1
• 计算第0行:f[0][0], f[0][1], …, f[0][n-1]
• 计算第1行:f[1][0], f[1][1], …, f[1][n-1]
• …• 计算第m-1行:f[m-1][0], f[m-1][1], …, f[m-1][n-1]
最小路径和
题目链接: link.
• 题意:
• 给定m行n列的网格,每个格子(i, j)里都一个非负数A[i][j]
• 求一个从左上角(0, 0)到右下角的路径,每一步只能向下或者向右走一步
• 使得路径上的格子里的数字之和最小
• 输出最小数字和
(1) 确定状态:分为最后一步以及子问题
最后一步:无论机器人用何种方式到达右下角,总有最后挪动的一步:向右 或者 向下右下角坐标设为(m-1, n-1),那么前一步机器人一定是在(m-2, n-1)或者(m-1, n-2)路径和最小
子问题为:机器人有从左上角走到(m-2, n-1)和(m-1, n-2)的路径和最小
(2) 写状态转移方程
(3) 规定初始条件以及边界情况
初始条件:初始条件:f[0][0] = A[0][0])
边界情况: i= 0 或 j = 0,则前一步只能有一个方向过来
(4) 计算顺序(消除冗余优化空间)
• f[0][0] = 1
• 计算第0行:f[0][0], f[0][1], …, f[0][n-1]