LeetCode Top100之62,64题

62. 不同路径

① 题目描述
  • 一个机器人位于一个 m × n m×n m×n网格的左上角 (起始点在下图中标记为“Start” )。
  • 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
  • 问总共有多少条不同的路径?
  • 例如,下图是一个 7 × 3 7×3 7×3的网格。有多少可能的路径?
    在这里插入图片描述
  • 说明:m 和 n 的值均不超过 100。
  • 示例 1:

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

  1. 向右 -> 向右 -> 向下
  2. 向右 -> 向下 -> 向右
  3. 向下 -> 向右 -> 向右
  • 示例 2:

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

② 使用带重复数字的全排列( Time Limit Exceeded
  • 通过观察发现:在m x n的方格中,从左上角到右下角,无论哪条路径都需要向右走m-1步,向下走n-1步。
  • 用0表示向右,1表示向下,问题就成了带重复数字的全排列问题。
  • 于是初始化一个(m-1)+(n-1)的数组,用于存储向右和向下的0和1,实现对该数组的全排列,记录排列的种数。
  • 可惜! Time Limit Exceeded
  • 代码如下:
public int uniquePaths(int m, int n) {
    
    
    int[] steps = new int[m + n - 2];
    int[] result = {
    
    0};
    for (int i = m - 1; i < steps.length; i++) {
    
    
        steps[i] = 1;
    }
    backtrace(steps, 0, result);
    return result[0];

}

public void backtrace(int[] nums, int cur, int[] result) {
    
    
    if (cur == nums.length) {
    
    
        result[0]++;
        return;
    }
    for (int i = cur; i < nums.length; i++) {
    
    
        if (isSwap(nums, cur, i)) {
    
    
            swap(nums, i, cur);
            backtrace(nums, cur + 1, result);
            swap(nums, i, cur);
        }
    }
}

public void swap(int[] nums, int i, int j) {
    
    
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

// 能不能交换,就是说交换数字中间不能出现与后面交换的重复数字如:122那个1只能和第一个2交换却不能和第二个2交换
public boolean isSwap(int[] num, int i, int j) {
    
    
    for (int index = i; index < j; index++) {
    
    
        if (num[index] == num[j])
            return false;
    }
    return true;
}
③ 递归(优化后,没有Time Limit Exceeded
  • ( 0 , 0 )点到( m - 1 , n - 1) 点的走法。
    (0,0)点到(m - 1 , n - 1) 点的走法等于(0,0)点右边的点 (1,0)(m - 1 , n - 1)的走法加上(0,0)点下边的点(0,1)(m - 1 , n - 1)的走法。
    ② 左边的点(1,0)点到(m - 1 , n - 1)点的走法等于(2,0)点到(m - 1 , n - 1)的走法加上(1,1)点到(m - 1 , n - 1)的走法。
    ③ 下边的点(0,1)点到(m - 1 , n - 1)点的走法等于(1,1)点到(m - 1 , n - 1)的走法加上(0,2)点到(m - 1 , n - 1)的走法。
    ④ 然后一直递归下去,直到 (m - 1 , n - 1) 点到(m - 1 , n - 1),返回 1。
  • 注意: 递归时,由于m和n不一定相等 ,所以需要判断递归时,right和down是否越界,如果越界则不能继续前进,设置其为0。
  • 可惜! 竟然Time Limit Exceeded,比我自己想的方法还死得早。。。。于是需进行优化,优化后竟然只需要3ms,神奇!!
  • 代码如下:
public int uniquePaths(int m, int n) {
    
    
    HashMap<String, Integer> visited = new HashMap<>();
    return stepSum(0, 0, m, n, visited);
}

public int stepSum(int right, int down, int m, int n, HashMap<String, Integer> visited) {
    
    
    if (right == m - 1 && down == n - 1) {
    
    
        return 1;
    }
    int right_sum = 0;
    int down_sum = 0;
    // 判断当前点是否已经求过了
    String key = (right + 1) + ", " + down;
    if (!visited.containsKey(key)) {
    
    
        if (right + 1 < m) {
    
    
            right_sum = stepSum(right + 1, down, m, n, visited);
        }
    } else {
    
    
        right_sum = visited.get(key);
    }
    key = right + ", " + (down + 1);
    if (!visited.containsKey(key)) {
    
    
        if (down + 1 < n) {
    
    
            down_sum = stepSum(right, down + 1, m, n, visited);
        }
    } else {
    
    
        down_sum = visited.get(key);
    }
    // 将当前点加入 visited 中
    visited.put(right + ", " + down, right_sum + down_sum);
    return right_sum + down_sum;
}
④ 动态规划
  • 第一行的格子、第一列的格子,只能通过向右、向下到达,设置为dp[i][0]=1dp[0][i]=1;
  • 其他位置的格子:dp[i][j]=dp[i-1][j]+dp[i][j-1];
  • 代码如下:
public int uniquePaths(int m, int n) {
    
    
    int[][] dp=new int[m][n];
    for(int i=0;i<m;i++){
    
    
        dp[i][0]=1;
    }
    for(int i=0;i<n;i++){
    
    
        dp[0][i]=1;
    }
    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];
}

64. 最小路径和

① 题目描述
  • 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
  • 说明:每次只能向下或者向右移动一步。
  • 示例:

输入:
[
    [1,3,1],
    [1,5,1],
    [4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1的总和最小。

② 动态规划
  • dp[i][j]表示从(0,0)(i,j)的最小路径总和,则有:
    ① 如果j - 1 >= 0,表示可以从(i,j)的左边(i,j - 1)向右到达(i,j);否则,设置dp[i][j - 1] = -1注意: 不要设置为0,因为grid中的数字可能为0。
    ② 如果i - 1 >= 0,表示可以从(i,j)的上边(i -1 ,j )向下到达(i,j);否则,设置dp[i - 1][j] = -1
    ③ 如果既能通过向下和向右到达(i,j),则取dp[i][j - 1]dp[i - 1][j]的最小值。
public int minPathSum(int[][] grid) {
    
    
    int m = grid.length;
    int n = grid[0].length;
    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];
                continue;
            }
            int right = (j - 1 >= 0) ? dp[i][j - 1] : -1;
            int down = (i - 1 >= 0) ? dp[i - 1][j] : -1;
            if (right == -1) {
    
    
                dp[i][j] = dp[i - 1][j] + grid[i][j];
            } else if (down == -1) {
    
    
                dp[i][j] = dp[i][j - 1] + grid[i][j];
            } else {
    
    
                dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
            }
        }
    }
    return dp[m - 1][n - 1];
}
③ 递归
  • 62题中求路径种数时,返回的是从(right,down)(m - 1,n - 1 )的路径总数。我们这里要求的是最小路径和,因此应该返回从(right,down)(m - 1,n - 1 )的最小路径和。
  • 注意更新(right,down)的最小路径和时,要加上自身的grid[right][down]
public int minPathSum(int[][] grid) {
    
    
    HashMap<String, Integer> visited = new HashMap<>();
    return minSum(0, 0, grid.length, grid[0].length, grid, visited);
}

public int minSum(int right, int down, int m, int n, int[][] grid, HashMap<String, Integer> visited) {
    
    
    if (right == m - 1 && down == n - 1) {
    
    
        return grid[m - 1][n - 1];
    }
    int right_sum = Integer.MAX_VALUE;
    int down_sum = Integer.MAX_VALUE;
    String key = (right + 1) + ", " + down;
    if (!visited.containsKey(key)) {
    
    
        if (right+1<m){
    
    
            right_sum = minSum(right + 1, down, m, n, grid, visited);
        }
    } else {
    
    
        right_sum = visited.get(key);
    }
    key = right + ", " + (down + 1);
    if (!visited.containsKey(key)) {
    
    
        if (down+1<n){
    
    
            down_sum = minSum(right, down + 1, m, n, grid, visited);
        }
    } else {
    
    
        down_sum = visited.get(key);
    }
    key = right + ", " + down;
    visited.put(key, Math.min(right_sum, down_sum) + grid[right][down]);
    return Math.min(right_sum, down_sum) + grid[right][down];
}

猜你喜欢

转载自blog.csdn.net/u014454538/article/details/90645666