女朋友也可以听得懂的动态规划之入门

200 阅读1分钟

路线

image.png

62.不同路径

力扣题目链接(opens new window)

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

示例 1:

输入:m = 3, n = 7 输出:28

示例 2: 输入:m = 2, n = 3 输出:3 解释: 从左上角开始,总共有 3 条路径可以到达右下角。

  1. 向右 -> 向右 -> 向下
  2. 向右 -> 向下 -> 向右
  3. 向下 -> 向右 -> 向右

示例 3: 输入:m = 7, n = 3 输出:28

示例 4: 输入:m = 3, n = 3 输出:6

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 2 * 10^9

思路

这是一题经典的动态规划,也展现动态规划最基本的思路,即状态转移

第一步 确定dp[i][j]的含义

dp[i][j]表示到达坐标(i,j)最多有几条线路

第二步 确定dp递推公式

我们看倒数第二个位置都来一定自于上方(i-1,j)和左方(i,j-1),所以我们可以假设 d[i][j]=d[i-1][j]+d[i][j-1]

第三步 初始化

但是我们就会发现dp[0][j]这一行和的d[i][0] 也就是最左边和上边是没有前项的,所以我们需要给出初始化值,由dp的含义我们可以知道,第一行的值都为1,因为只有从左向右这一条路,第一列同理

第四步 确定遍历顺序

因为起点已经决定好了,所以我们的遍历方向已经决定是从左向右,从上至下

第五步 试写dp

image.png

成功!

代码实现

class Solution {
    public int uniquePathsWithObstacles(int m, int n) {
       int dp[][]=new int[m+1][n+1];
       for(int i=0;i<m;i++)
           dp[i][0]=0;
        for(int i=0;i<n;i++)
            dp[0][i]=0;
        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];
    }
}

62.不同路径II

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

示例 1:

输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]] 输出:2 解释: 3x3 网格的正中间有一个障碍物。 从左上角到右下角一共有 2 条不同的路径:

  1. 向右 -> 向右 -> 向下 -> 向下
  2. 向下 -> 向下 -> 向右 -> 向右

示例 2:

输入:obstacleGrid = [[0,1],[0,0]] 输出:1

提示:

  • m == obstacleGrid.length
  • n == obstacleGrid[i].length
  • 1 <= m, n <= 100
  • obstacleGrid[i][j] 为 0 或 1

思路

这是一题经典的动态规划,也展现动态规划最基本的思路,即状态转移,比上面那道题目而言,多了一个条件,加入了障碍物。但实际也很好解决

第一步 确定dp[i][j]的含义

dp[i][j]表示到达坐标(i,j)最多有几条线路

第二步 确定dp递推公式

如果(i,j)不是障碍物 我们看倒数第二个位置都来一定自于上方(i-1,j)和左方(i,j-1),所以我们可以假设 d[i][j]=d[i-1][j]+d[i][j-1] 如果是障碍物 d[i][j]=0;

第三步 初始化

但是我们就会发现dp[0][j]这一行和的d[i][0] 也就是最左边和上边是没有前项的,所以我们需要给出初始化值,由dp的含义我们可以知道,第一行的值都为1,因为只有从左向右这一条路,第一列同理,但是有了障碍物,则之后的道路都不通了 所以 for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) dp[i][0] = 1;

第四步 确定遍历顺序

因为起点已经决定好了,所以我们的遍历方向已经决定是从左向右,从上至下

第五步 试写dp

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m=obstacleGrid.length;
        int n=obstacleGrid[0].length;
        int dp[][]=new int[m+1][n+1];
        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;
        for(int i=1;i<m;i++)
        {for(int j=1;j<n;j++)
        {
            if(obstacleGrid[i][j]==1)
                continue;
            dp[i][j]=dp[i-1][j]+dp[i][j-1];
        }
        }
        return dp[m-1][n-1];
    }
}

整数拆分

给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。

示例 1: 输入: 2 输出: 1

\解释: 2 = 1 + 1, 1 × 1 = 1。

示例 2: 输入: 10 输出: 36 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。 说明: 你可以假设 n 不小于 2 且不大于 58。

思路

第一步 确定dp[i][j]的含义

dp[i]表示i最多的能拆分出的正整数和;

第二步 确定dp递推公式

我们回到无穷远处,可以得知,dp[i]的值有两种可能,两位数相乘: (i-j)*j 或者是两位以上相乘 dp[i-j]*j 所以dp[i]=max(dp[i-j]*j,(i-j)*j) 但是这么做还是有问题,就是算出的值不一定是一次里最大的值 所以dp[i]=max(dp[i],dp[i-j]*j,(i-j)*j)

第三步 初始化

dp[2]=1

第四步 确定遍历顺序

因为起点已经决定好了,所以我们的遍历方向已经决定是从左向右,从上至下

第五步 试写dp

class Solution {
    public int integerBreak(int n) {
        int dp[]=new int[n+1];
        dp[2]=1;
        for(int i=2;i<=n;i++)
            for(int j=0;j<i;j++)
                dp[i]=Math.max(dp[i],Math.max(dp[i-j]*j,(i-j)*j));
        return dp[n];
    }
}
不同的二叉搜索树

力扣题目链接(opens new window)

给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?

示例:

思路

第一步确定dp[i]定义

dp[i],n=i时,共有多少种二叉搜索树

第二步确定递推公式

dp[i]=dp[i的左子树]形态*右子树形态,什么意思呢?我们可以看下递推公式

dp[0]=1;

dp[1]=1;

dp[2]=2

dp[3]=dp[2]+dp[0]+dp[1]+dp[1]

dp[4]=dp[3]*dp[0]+dp[2]*dp[1]+dp[1]*dp[2]

所以dp[i]+=dp[i-j]*d[j-1]

第三步初始化

由递推公式可以得出,dp[i]依赖于i-j,所以dp[0]是初始项,所以dp[0]为0个节点的搜索树,0个节点只有一钟搜索树 所以dp[0]=1;

第四步确定遍历顺序

由前开始所以,i>j>1,i从1开始;j从1开始

第五步

上文已经写过dp数组了

代码实现

class Solution {
    public int numTrees(int n) {
     int dp[]=new int[n];
     dp[0]=1;
     if(n==0)
         return dp[0];
     for(int i=1;i<=n;i++)
         for(int j=1;j<=i;j++)
             dp[i]+=dp[i-j]*dp[j];

     return dp[n];
    }
}