LeetCode-62. Unique Paths(不同的路径数量)(简单dp)

LeetCode-62. Unique Paths(不同的路径数量)(简单dp)

  • 递归(超时)
  • 记忆化
  • 二维dp
  • 空间优化

题目链接

这个题目和最小路径和问题很类似。

递归(超时)

来到某个结点,因为只能往右边和下面走,所以一个位置[i,j]依赖的位置是[i-1,j]和[i,j-1]位置两个点之前的数量之和; 所以我们把[i,j]看做是终点的话就得出下面的递归关系。
这里写图片描述
如果不记录重复子问题的话就会是O(2^n);

    public int uniquePaths(int m, int n) {
        return process(m,n,m,n);
    }

    public static int process(int m,int n,int i,int j){
        if(i == 1 && j == 1){ //左上角
            return 1;
        }
        if(i == 1){  //第一行
            return process(m,n,i,j-1);
        }
        if(j == 1){  //第一列
            return process(m,n,i-1,j);
        }
        return process(m,n,i-1,j) + process(m,n,i,j-1);
    }

记忆化

上面的方法计算了很多的重复子问题,我们可以使用一个二维数组保存已经算过的子问题,一旦发现已经算过,就不再重复求解。

    private int[][] map;
    public int uniquePaths(int m, int n) {
        map = new int[m+1][n+1];
        return process(m,n,m,n);
    }
    public int process(int m,int n,int i,int j){
        if(i == 1 && j == 1){
            return 1;
        }
        if(map[i][j] != 0){//已经计算过
            return map[i][j];
        }
        if(i == 1){
            map[i][j] = process(m,n,i,j-1);
            return map[i][j];
        }
        if(j == 1){
            map[i][j] = process(m,n,i-1,j);
            return map[i][j];
        }
        map[i][j] = process(m,n,i-1,j) + process(m,n,i,j-1);
        return map[i][j];
    }

二维dp

递归改动态规划就是和递归相反的方向:
这里写图片描述
如果是上面的递归改成动态规划就是:

    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m+1][n+1];
        dp[1][1] = 1;
        for(int j = 2; j <= n; j++)dp[1][j] = dp[1][j-1]; //第一行
        for(int i = 2; i <= m; i++)dp[i][1] = dp[i-1][1];
        for(int i = 2; i <= m; i++){
            for(int j = 2; j <= n; j++){
                dp[i][j] = dp[i][j-1] + dp[i-1][j];
            }
        }
        return dp[m][n];
    }

可以有两种写法,在于你看问题的角度:

  • 第一种看问题的角度,把[i,j]看做是终点,那就是上面的递归关系;
  • 第二种看问题的角度,把[i,j]看做是起点,此时[i,j]总共的数量就是从[i+1,j]出发和从[i,j+1]出发的数量,那就是下面的递归关系;

就是说递归和动态规划也可以写成这样:


    public int uniquePaths(int m, int n) {
        return proc(m,n,1,1);
    }
    public static int proc(int m,int n,int i,int j){
        if(i == m && j == n){
            return 1;
        }
        if(i == m){
            return proc(m,n,i,j+1);
        }
        if(j == n){
            return proc(m,n,i+1,j);
        }
        return proc(m,n,i+1,j) + proc(m,n,i,j+1);
    }

然后动态规划的顺序:
这里写图片描述

 public int uniquePaths(int m, int n) {
        int[][] dp = new int[m+1][n+1];
        dp[m][n] = 1;
        for(int j = n-1; j >= 1; j--)dp[m][j] = dp[m][j+1]; 
        for(int i = m-1; i >= 1; i--)dp[i][n] = dp[i+1][n];
        for(int i = m-1; i >= 1; i--){
            for(int j = n-1; j >= 1; j--){
                dp[i][j] = dp[i][j+1] + dp[i+1][j];
            }
        }
        return dp[1][1];
    }

滚动优化

滚动数组的优化就是其实你在算dp[i][j]的时候,你左边的dp[i][j-1]还是dp[j-1],而你上面的dp[i-1][j]还是dp[j] (没有更新),所以可以只需要一个数组,所以滚动优化决定的是你更新的顺序;

     public int uniquePaths(int m,int n){
        int[] dp = new int[n + 1];
        for(int i = 1; i <= n; i++)dp[i] = 1;
        for(int i = 2; i <= m; i++){
            for(int j = 2; j <= n; j++){
                dp[j] = dp[j] + dp[j-1];
            }
        }
        return dp[n];
    }

或者这样 (第二种):

    public int uniquePaths(int m,int n){
        int[] dp = new int[n + 1];
        for(int i = 1; i <= n; i++)dp[i] = 1;
        for(int i = m-1; i >= 1; i--){
            for(int j = n-1; j >= 1; j--){
                dp[j] = dp[j] + dp[j+1];
            }
        }
        return dp[1];
    }

猜你喜欢

转载自blog.csdn.net/zxzxzx0119/article/details/81807910