round1/day13补/动规2

1. 关于动规

还是要建立dp数组思维
dp数组的当前步和上一步:之间是什么关系?上一步中哪些元素共同组成了当前步的结果?

动规的应用场景

tbc

2. 例题

lc62 不同路径

思路

  1. 观察动作:每次只能走一步
  2. dp数组是什么?——dp[i][j]记录从[0][0]到点[i][j]的所有方法数量
  3. dp公式是什么?怎么从上一步到当前步?——两种情况到点[i][j],从[i-1][j]向右一步,或从[i][j-1]向下一步,即点[i][j]的方法数=到点[i-1][j]的方法数+到点[i][j-1]的方法数
  4. 初始值是什么?——两条边线:在第一行和第一列的所有点,到这些点都只有一种方法,即[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

易错点

  1. 如果起点和终点有障碍物?
  2. 如果在初始化行列上有障碍物?
  3. 如果在其他点上有障碍物?
  4. 注意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 整数拆分

思路

  1. dp数组是什么? ——dp[n]储存的是n拆解出的最大乘积。不要忘记这个数组的定位是:最大乘积!
  2. 上一步和当前数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的值
  3. 初始值是什么?——从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叉树之间的规律。

  1. dp数组:当整数位n时,不同排列组合的数量为dp[n]
  2. 这一步和上一步的关系: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
  3. 初始数组: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];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值