1. 关于动规
还是要建立dp数组思维
dp数组的当前步和上一步:之间是什么关系?上一步中哪些元素共同组成了当前步的结果?
动规的应用场景
tbc
2. 例题
lc62 不同路径
思路
- 观察动作:每次只能走一步
- dp数组是什么?——dp[i][j]记录从[0][0]到点[i][j]的所有方法数量
- dp公式是什么?怎么从上一步到当前步?——两种情况到点[i][j],从[i-1][j]向右一步,或从[i][j-1]向下一步,即点[i][j]的方法数=到点[i-1][j]的方法数+到点[i][j-1]的方法数
- 初始值是什么?——两条边线:在第一行和第一列的所有点,到这些点都只有一种方法,即[i][0]和[0][j]
代码实现
class Solution {
public int uniquePaths(int m, int n) {
//创建dp数组
int[][] dp=new int[m][n];
//初始化
for(int i=0;i<m;i++) dp[i][0]=1;
for(int j=0;j<n;j++) dp[0][j]=1;
//dp公示,从上一步到现在
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
return dp[m-1][n-1];
}
}
lc63 不同路径II
前置知识点
- 获取多维数组的行列:
行=obstacleGrid.length;
列=obstacleGrid[0].length;
思路
与62的dp数组思路相同,增加了障碍物的检测
检测障碍物:若obstacleGrid[i][j]==1,则令dp[i][j]==0
易错点
- 如果起点和终点有障碍物?
- 如果在初始化行列上有障碍物?
- 如果在其他点上有障碍物?
- 注意obstacleGrid[i][j]和dp[i][j]是两码事
代码实现
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
//创建dp数组
int m=obstacleGrid.length;
int n=obstacleGrid[0].length;
int[][] dp=new int[m][n];
//易错点1:如果起终点有障碍?
if(obstacleGrid[0][0]==1 || obstacleGrid[m-1][n-1]==1){
return 0;
}
//初始化
//易错点2:如果两条边线上有阻碍?从阻碍之后开始的每个点方法都是0个
for(int i=0;i<m && obstacleGrid[i][0]==0 ;i++){
dp[i][0]=1;
}
for(int j=0;j<n && obstacleGrid[0][j]==0;j++){
dp[0][j]=1;
}
//dp公式:从上一步到现在
for(int a=1;a<m;a++){
for(int b=1;b<n;b++){
if(obstacleGrid[a][b]==1){
dp[a][b]=0;
}else{
dp[a][b]=dp[a-1][b]+dp[a][b-1];
}
}
}
return dp[m-1][n-1];
}
}
lc343 整数拆分
思路
- dp数组是什么? ——dp[n]储存的是n拆解出的最大乘积。不要忘记这个数组的定位是:最大乘积!
- 上一步和当前数dp[n]的关系是什么?——上一步可以是谁?不一定是dp[n-1]才叫做上一步!
- 情况1:dp[i-j]*j
- 情况2:(i-j)*j ——*为什么这种情况不会存在在dp[i-j]j里呢?因为如果i-j要取最大数即0和i-j的话,它的乘积是0,所以没办法测试i-j的值
- 初始值是什么?——从2开始计算
代码实现
class Solution {
public int integerBreak(int n) {
//建立数组dp,因为n最大可以拆成n和0,所以长度是n+1
int[] dp= new int[n+1];
//初始化
dp[2]=1;
//上一步和当前数之间差个乘数m:dp[n-m]*m=dp[n]——谁才是m?从1开始到n-1为止,一个个试过来
for(int i=3;i<=n;i++){ //从3开始遍历到正整数n为止
for(int j=1;j<=i-j;j++){ //这里是对每个正整数i,都拆分成dp[前一个最大乘积-最佳乘数]*最佳乘数,以求得当前数i的最大乘积
//注意,这里的每一个j都会产生一个新的乘积dp[i],所以这些乘积之间也要比较出max
dp[i]=Math.max(dp[i],Math.max(dp[i-j]*j,(i-j)*j));
}
}
return dp[n];
}
}
lc96 不同的二叉搜索树
前置知识点
tbc
思路
难点在寻找n-1叉树和n叉树之间的规律。
- dp数组:当整数位n时,不同排列组合的数量为dp[n]
- 这一步和上一步的关系:n时,比起n-1,新增strat=n的节点,即新增dp[n-1]种,同时,剩余start=1~n-1的节点,原方法数为dp[n-1]种,现方法数增加n-2种。故dp[n]=2*dp[n-1]+n-2
- 初始数组:dp[1]=1,dp[2]=2,从dp[3]开始算
代码实现
class Solution {
public int numTrees(int n) {
//建立数组dp
int[] dp=new int[n+1];
//初始化
dp[0]=1;
//这一步和上一步的关系
//以j为头节点的二叉树,每一个的数量dp[i]为dp[j左子树数量]*dp[j右子树数量],遍历j,直到求出从1-i为头节点的数量总和。此时的总和即为当n=i时,一共的数量。然后从1遍历到n,求n时的组合数量
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
dp[i]=dp[i]+ dp[j-1]*dp[i-j];
}
}
return dp[n];
}
}