矩阵的最小路径和 & 地下城游戏

280 阅读3分钟

前言

“这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战

  1. NC59 矩阵的最小路径和
  2. 174. 地下城游戏

矩阵的最小路径和

描述

给定一个 n * m 的矩阵 a,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,输出所有的路径中最小的路径和。

示例1

输入:

[[1,3,5,9],[8,1,3,4],[5,0,6,1],[8,8,4,0]]

返回值:

12

备注:

1  20001≤n,m≤2000
1  1001≤arri,j​≤100

思路分析:

dp[i][j] 表示 走到 matrix[i][j] 时的最小路径和, 因为每次只能向右或者向下走

所以当前位置 (i,j), 是由 位置(i-1.j) 或者 位置(i,j-1) 转移过来的

因为要找的时最小路径和,

所以 dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + matrix[i][j]

注意数组越界的问题

impicture_20210824_150105.png

AC 代码

import java.util.*;


public class Solution {
    /**
     * 
     * @param matrix int整型二维数组 the matrix
     * @return int整型
     */
    public int minPathSum(int[][] matrix) {
        // write code here

        int m = matrix.length;
        int n = matrix[0].length;

        int[] dp = new int[n];

        for (int j = 0; j < n; j++)
            dp[j] = j > 0 ?
                    dp[j - 1] + matrix[0][j] : matrix[0][j];

        for (int i = 1; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int left = j > 0 ? dp[j - 1] : Integer.MAX_VALUE;
                dp[j] = matrix[i][j] + Math.min(left, dp[j]);
            }
        }

        return dp[n - 1];
    }
}

地下城游戏

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

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

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

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

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

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

-2 (K) -3 3

-5 -10 1

10 30 -5 (P)  

说明:

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

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

思路分析

m 行 n 列

dp[i][j]代表从(i,j)到终点的初始最低血量

因为任何房间都可以对骑士的健康点数产生影响,并且健康点数在某一时刻降至 0 或以下,他会立即死亡

可以计算 dp[m - 1][n - 1] 最后一个房间的初始值

  • arr[m - 1][n - 1] >= 0 , 这个房间 加血, 所以 初始血量 最低为 1
  • arr[m - 1][n - 1] < 0, 这个房间减血, 减完之后最低为 1 , 那么初始血量 为 1 - arr[m - 1][n - 1]

骑士只可以向下 or 向右移动

因此 通过最后一个房间的初始血量,可以逆推 dp[m-1][n-2] 和 dp[m-2][n-1] 的值

dp[i][j] 的值 取决与 dp[i][j + 1] 和 dp[i+1][j]的值 状态转移方程

dp[i][j] = Math.max(1, Math.min(dp[i][j + 1] , dp[i+1][j]) - arr[i][j])

因为 dp[i][j] 只与 (i,j) 右边和下面相关, 并且不需要记录选择的路径, 所以 dp 数组可以压缩为 1 维

AC 代码

    public int calculateMinimumHP(int[][] dungeon) {

        int m = dungeon.length;
        int n = dungeon[0].length;


        int[] dp = new int[n];
        dp[n - 1] = Math.max(1, 1 - dungeon[m - 1][n - 1]);


        for (int j = n - 2; j >= 0; j--) dp[j] = Math.max(1, dp[j + 1] - dungeon[m - 1][j]);

        for (int i = m - 2; i >= 0; i--) {
            for (int j = n - 1; j >= 0; j--) {
                int right = j < n - 1 ? Math.max(1, dp[j + 1] - dungeon[i][j]) : Integer.MAX_VALUE;
                dp[j] = Math.min(right, Math.max(1, dp[j] - dungeon[i][j]));
            }
        }

        return dp[0];
    }