Leetcode: Unique Path I & II, Minimum Path Sum, Triangle

这四个题都是比较直观的DP题,题目中已经暗示了我们怎么Cache递归表达式算出来的数据,我们可以采取和题目中一样的矩阵,或者节省一部分空间采用滚动数组解题

Problem 1: Unique Path I

A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).

How many possible unique paths are there?


Above is a 3 x 7 grid. How many possible unique paths are there?

Solve the Problem on Leetcode

分析:

这个机器人只能往右或者往下走,那么对于一个给定点坐标dp[i][j],它的来源只有两个方向,dp[i-1][j]和dp[i][j-1],我们只需要把这两个坐标里的值加起来即可。唯一要注意的是小心边界上的条件,对于第一行来说,机器人只能通过往右走到达,所以第一行每一个格子只可能有一种路径。同理对于第一列来说,机器人只能通过往下走到达,所以第一列里每一个格子也只有一种路径

public class Solution {
    public int uniquePaths(int m, int n) {
        if (m==0 || n==0) return 0;
        int[][] dp = new int[m][n];
        for(int i=0;i<m;i++){
            dp[i][0] = 1;
            for(int j=1;j<n;j++){
                if (i==0) dp[i][j] = 1;
                else dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
}

有没有节省空间的方法呢?有的,观察上面程序可以发现循环是从上到下扫描行的,在每一行里,我们又是从左到右来扫描的,我们完全可以用一个滚动数组来表示每一行,算下一行的时候,只需要更新这个数组里的值便可以了。

public class Solution {
    public int uniquePaths(int m, int n) {
        if (m==0 || n==0) return 0;
        int[] dp = new int[n];
        dp[0] = 1;
        for(int i=0;i<m;i++){
            for(int j=1;j<n;j++){
                if (i==0) dp[j] = 1;
                else dp[j] = dp[j] + dp[j-1];
                }
            }
        return dp[n-1];
    }
}

注意程序里的这一句 dp[j] = dp[j] + dp[j-1] 当我们复写这一层的dp[j]之前,dp[j]还是上一行的值,dp[j-1]是左边格子我们复写后的值,所以加起来等价于二维数组里面 dp[i][j] = dp[i-1][j] + dp[i][j-1]

Problem 2: Unique Path II

Follow up for "Unique Paths":

Now consider if some obstacles are added to the grids. How many unique paths would there be?

An obstacle and empty space is marked as 1 and 0 respectively in the grid.

For example,

There is one obstacle in the middle of a 3x3 grid as illustrated below.

[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]

The total number of unique paths is 2.

Note: m and n will be at most 100.

Solve the Problem on Leetcode

分析:

这一道题在上一题的基础上,多了障碍物,当我们碰到障碍物的时候,我们无论怎么走也不可能走到这个格子里,所以在这个格子里,累加的路径值为0

public class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        if (m==0||n==0) return 0;
        if (obstacleGrid[0][0] == 1) return 0;
        int[][] dp = new int[m][n];
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if (obstacleGrid[i][j]==0){
                    if (i==0 && j==0) dp[i][j] = 1;
                    else dp[i][j] = (i>0 ? dp[i-1][j] : 0) + (j>0 ? dp[i][j-1] : 0);
                }
            }
        }
        return dp[m-1][n-1];
    }
}

为了简化,我们只需计算obstacleGrid矩阵里,值为0的格子就可以了,其余的格子便是障碍物,会被dp矩阵初始化为0

注意这一句

dp[i][j] = (i>0 ? dp[i-1][j] : 0) + (j>0 ? dp[i][j-1] : 0);

其实等价于下面这三句,为了偷懒我把它打成一句了

i = 0 dp[i][j] = dp[i][j-1];
j = 0 dp[i][j] = dp[i-1][j];
i >0 j>0 dp[i][j] = dp[i][j-1] + dp[i-1][j]

用滚动数组的解法:

public class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        if (m==0||n==0) return 0;
        if (obstacleGrid[0][0] == 1) return 0;
        int[] dp = new int[n];
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if (obstacleGrid[i][j]==0){
                    if (i==0 && j==0) dp[j] = 1;
                    else dp[j] = (i>0 ? dp[j] : 0) + (j>0 ? dp[j-1] : 0);
                }else dp[j] = 0; //手动给障碍物格子赋值为0
            }
        }
        return dp[n-1];
    }
}

唯一注意的一点,在矩阵里,我们不用管obstacleGrid里面为1的障碍点,是因为矩阵里默认初始化值为0,但是在滚动数组里我们要复用之前数组里的值,所以我们要处理每一个矩阵中的格子,我们不能跳过障碍物的格子了,所以要手动给它们赋值为0.


Problem 3 Minimum Path Sum

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.

Note: You can only move either down or right at any point in time.

Solve the Problem on Leetcode

分析:

同样的思路,刚才我们是求累加的值,现在我们只不过求最小的值,对于dp[i][j]的两个来源,我们只要取最小值再加上这一点的值便可以了。需要注意一点,如果你也用我刚才偷懒的语句写:

dp[i][j] = grid[i][j] + Math.min((i>0?dp[i-1][j]:Integer.MAX_VALUE),(j>0?dp[i][j-1]:Integer.MAX_VALUE));

这一句,要把之前的0,换成Integer.MAX_VALUE

public class Solution {
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        if (m==0||n==0) return 0;
        int[][] dp = new int[m][n];
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++){
                if (i==0 && j==0) dp[0][0] = grid[0][0];
                else dp[i][j] = grid[i][j] + Math.min((i>0?dp[i-1][j]:Integer.MAX_VALUE),(j>0?dp[i][j-1]:Integer.MAX_VALUE));
            }
        return dp[m-1][n-1];       
        }
}

用滚动数组的解:

public class Solution {
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        if (m==0||n==0) return 0;
        int[] dp = new int[n];
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++){
                if (i==0 && j==0) dp[0] = grid[0][0];
                else dp[j] = grid[i][j] + Math.min((i>0?dp[j]:Integer.MAX_VALUE),(j>0?dp[j-1]:Integer.MAX_VALUE));
            }
        return dp[n-1];       
        }
}


Problem 4: Triangle

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.

For example, given the following triangle

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).

Note:
Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.

Solve the Problem on Leetcode

分析:

这道题看上去和矩阵的形式不同,但是我们换个写法就能发现玄机了


首先可以观察到,边界和之前的题不同了,之前的题,边界是 i = 0 , j = 0 而在这道题里边界变为了i = 0 , i = j

接下来我们看对于非边界上的点来怎么推算,图里显示的很清楚,来自上方的格子,和左上角的格子

即: 对于dp[i][j] = dp[i-1][j-1] + dp[i-1][j]

对于左边边界i=0,只有上方一个来源 dp[i][j] = dp[i-1][j]

对于右边边界j = i,只有斜上角一个来源 dp[i][j] = dp[i-1][j-1]

还有一点需要注意的是,在上面的题目中,我们最后只要求得最右下角的值就行了,而对于Triangle,我们求的是最小和路径,路径可以通过最后一行里任意一个元素出去,所以在算出最后一行值后,要求出其中的最小值

public class Solution {
    public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
        if (triangle==null) return 0;
        int n = triangle.size();
        int[][] dp = new int[n][n];
        for(int i=0;i<n;i++)
            for(int j=0;j<i+1;j++)
                if(i==0 && j==0) dp[0][0] = triangle.get(0).get(0);
                else dp[i][j] = triangle.get(i).get(j) + Math.min((i!=j?dp[i-1][j]:Integer.MAX_VALUE),(j>0?dp[i-1][j-1]:Integer.MAX_VALUE));
        Arrays.sort(dp[n-1]);
        return dp[n-1][0];
    }
}


好了,和矩阵有关的DP基础就是这些,自己试着把最后一题Triangle改写成O(n) space的试试?



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值