leetcode动态规划——网格型

网格型动态规划特点

题干大体为
给定一个二维数组,从左上角出发,每次只能向下或向右走一格,走到右下角。
再根据具体条件求出结果,比如:每个网格加入权值,求最短路径;某网格不能通行,求最短路径等;

思路

  1. 构造一个二维数组存储当前最优解;
  2. 采用自底向上的思考顺序,写出通式;
  3. 边界问题;

62. 不同路径

一个机器人位于一个 m x n 网格的左上角)。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角。问总共有多少条不同的路径?

说明:m 和 n 的值均不超过 100。

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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/unique-paths

public int uniquePaths(int m, int n) {
		//存储 到达当前位置所需路径的 条数
        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[i][j] = 1;
                }else{
                    //其他位置等于上和左的总和
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                }
            }
        return dp[m - 1][n - 1];
    }

63. 不同路径 II

一个机器人位于一个 m x n 网格的左上角 。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
说明:m 和 n 的值均不超过 100。

示例 1:

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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/unique-paths-ii

  1. 用 dp[][] 来表示到达当前位置的路径条数。
  2. 通式 dp[m][n] = dp[m - 1][n] + dp[m][n - 1]
  3. 边界问题
    1. 若起点有障碍,则直接返回0;
    2. 第一行、第一列:除去起点,若当前位置有障碍,则置为0;否则置为上一个位置路径数。
      对于第一行 dp[0][i] = gjrid[0][i] == 1 ? 0 : dp[0][i - 1]
      对于第一列 dp[j][0] = grid[j][0] == 1 ? 0 : dp[j - 1][0]
    3. 其他位置:若当前位置有障碍,则置为0;否则置为上与左路径数之和。
      dp[j][i] = grid[j][i] == 1 ? 0 : p[j][i - 1] + dp[j - 1][i]
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        
        if(obstacleGrid[0][0] == 1)return 0;
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        //用于存储 到达当前位置所需路径的 条数
        int[][] array = new int[m][n];
        int i,j;
        //起始位置赋值
        array[0][0] = 1;
        //边界 第一列赋值
        for(i = 1, j = 0;i < m ; i++){
    	    array[i][j] = obstacleGrid[i][j] == 1 ? 0 : array[i-1][j];
        }
        //边界 第一行赋值
        for(i = 0, j = 1 ;j < n ; j++){
        	array[i][j] = obstacleGrid[i][j] == 1 ? 0 : array[i][j-1];
        }
        for(i = 1;i<m;i++)
            for(j = 1 ; j < n; j++){
            array[i][j] = obstacleGrid[i][j] == 1 ? 0 : array[i][j-1] + array[i-1][j];
           }
        return array[m-1][n-1];
    }

64. 最小路径和

给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

示例:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-path-sum

  1. 用 dp[][] 来表示到达当前位置的路径最小值。
  2. 通式 dp[m][n] = dp[m - 1][n] + dp[m][n - 1]
  3. 边界问题
    1. dp[0][0] = grid[0][0];
    2. 第一行、第一列:除去起点,当前位置dp等于上一个位置的dp + 当前位置的权值grid[][]。
      对于第一行 dp[0][i] = grid[0][i] + dp[0][i - 1]
      对于第一列 dp[j][0] = grid[j][0] + dp[j - 1][0]
    3. 其他位置:当前位置dp[i][j] = min{ dp[i - 1][j], dp[i][j - 1] } +grid[i][j] 。
public int minPathSum(int[][] grid) {

        int row = grid.length;
        int line = grid[0].length;
        int[][] f = new int[row][line];

        f[0][0] = grid[0][0];
        for (int i = 1; i < row; i++){
            f[i][0] = grid[i][0] + f[i - 1][0];
        }
        for (int i = 1; i < line; i++){
            f[0][i] = grid[0][i] + f[0][i - 1];
        }

        for(int i = 1; i < row; i++)
            for(int j = 1; j < line; j++){
                f[i][j] = grid[i][j] + Math.min(f[i - 1][j],f[i][j - 1]);
            }
        return f[row - 1][line - 1];
    }

174. 地下城游戏

一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。

骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。

有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。

为了尽快到达公主,骑士决定每次只向右或向下移动一步。

编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。

例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。

abc
-2 (K)-33
-5-101
1030-5 (\P)

说明:

骑士的健康点数没有上限。

任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/dungeon-game

分析:若骑士能救出公主,则必须保证经过p点后血量至少为0(便于操作,程序返回时再加1)。此题要从p到k的顺序思考,即从右下向左上走,每次只能向左或上移动一格。
当前位置为负数-x,证明要掉血x,由于预知了之后要损失的血量n,所以当前应需x + n的血量。
当前位置为正数,证明要加血x,由于预知了之后要损失的血量n,所以当前应需n - x的血量。若x >= n ,当前应需的血量为0。

  1. 用 dp[][] 来表示到达当前位置至少需要 的血量。
  2. 通式
if(grid[i][j] < 0) {
dp[i][j] = Math.min(dp[i - 1][j] + dp[i][j - 1]) - grid[i][j]
}else if(grid[i][j] >= Math.min(dp[i - 1][j] + dp[i][j - 1])){
dp[i][j] = 0;
}else{
dp[i][j] = Math.min(dp[i - 1][j] + dp[i][j - 1] - grid[i][j];
}
//简化为以下代码
dp[i][j] =  grid[i][j] >= Math.min(dp[i + 1][j],dp[i][j + 1]) ? 
                0 : Math.min(dp[i + 1][j],dp[i][j + 1]) - grid[i][j];
  1. 边界问题
    1. 若grid[m][n] >= 0,则dp[m][n] = 0; 否则dp[m][n] = - grid[m][n]
public int calculateMinimumHP(int[][] dungeon) {
        
        int row = dungeon.length;
        int line = dungeon[0].length;
        int[][] dp = new int[row][line];
		//起始位置初始化
        dp[row - 1][line - 1] = Math.max(0,-dungeon[row - 1][line - 1]);
		//最后一列
        for(int i = row - 2; i >= 0; i--){
            dp[i][line - 1] = dungeon[i][line - 1] >= dp[i + 1][line - 1] ? 
            0 : dp[i + 1][line - 1] - dungeon[i][line - 1];
        }
		//最后一行
        for(int j = line - 2; j >= 0; j--){
            dp[row - 1][j] = dungeon[row - 1][j] >= dp[row - 1][j + 1] ?
            0 : dp[row - 1][j + 1] - dungeon[row - 1][j];
        }

        for(int i = row - 2; i >= 0; i--)
            for(int j = line - 2; j >= 0; j--){
               
                dp[i][j] =  dungeon[i][j] >= Math.min(dp[i + 1][j],dp[i][j + 1]) ? 
                0 : Math.min(dp[i + 1][j],dp[i][j + 1]) - dungeon[i][j];
            }
		//返回值要加1
        return dp[0][0] + 1;        
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值