Code44 骑士救公主问题

337 阅读2分钟

题目描述

  • 给定一个二维数组 map ,含义是一张地图,例如,如下矩阵:
  • —2, —3, 3
  • —5, —10, 10
  • 3, 0, —5
  • 游戏的规则如下:
  • 骑士从左上角出发、每次只能向右或向下走,最后到达右下角见到公主。
  • 地图中每个位置的值代表骑士要遭遇的事情
  • 如果是负数,说明此处有怪兽,要让骑士损失血量。
  • 如果是非负数,代表此处有血瓶,能让骑士回血。
  • 骑士从左上角到右下角的过程中,走到任何一个位置时,血量都不能少于1。
  • 为了保证骑士能见到公主,初始血量至少是多少?
  • 根据 map ,返回至少的初始血量。

code

public class Code44 {
    public static int minHP(int[][] matrix) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return 0;
        }
        int N = matrix.length;
        int M = matrix[0].length;
        return process(matrix, N, M, 0, 0);
    }

    // 来到了matrix[row][col],还没登上去,到达右下角,返回至少的初始血量
    private static int process(int[][] matrix, int N, int M, int row, int col) {
        // 即将来到右下角 需要的最少血量
        if (row == N - 1 && col == M - 1) {
            if (matrix[row][col] >= 0) {
                // 只要有1点血量迈上去即可
                return 1;
            } else {
                // 负数 -3 -> 4  -(-3) + 1o
                return -matrix[row][col] + 1;
            }
        }

        // 只能往右走
        if (row + 1 == N) {
            int right = process(matrix, N, M, row, col + 1);
            // 如果当前位置的血量 大于等于 往右的所需要最少血量 直接返回1 踏上当前位置即可
            int cur = matrix[row][col];
            if (cur >= right) {
                return 1;
            } else {
                // 先登上 当前位置 + 往右的所需要最少血量
                return -cur + right;
            }
        }
        // 只能往下走
        if (col + 1 == M) {
            int down = process(matrix, N, M, row + 1, col);
            // 如果当前位置的血量 大于等于 往右的所需要最少血量 直接返回1 踏上当前位置即可
            int cur = matrix[row][col];
            if (cur >= down) {
                return 1;
            } else {
                // 先登上 当前位置 + 往下的所需要最少血量
                return -cur + down;
            }
        }

        // 都可以走
        int nextNeed = Math.min(process(matrix, N, M, row + 1, col), process(matrix, N, M, row, col + 1));
        int cur = matrix[row][col];
        if (cur >= nextNeed) {
            return 1;
        } else {
            // 先登上 当前位置 + 往右(下)的所需要最少血量
            return -cur + nextNeed;
        }
    }

    // 每个位置只依赖他 右边的 下面的 右下角的位置
    public static int minHP1(int[][] m) {
        if (m == null || m.length == 0 || m[0] == null || m[0].length == 0) {
            return 1;
        }
        int row = m.length;
        int col = m[0].length;
        int[][] dp = new int[row--][col--];
        dp[row][col] = m[row][col] > 0 ? 1 : -m[row][col] + 1;
        for (int j = col - 1; j >= 0; j--) {
            dp[row][j] = Math.max(dp[row][j + 1] - m[row][j], 1);
        }
        int right = 0;
        int down = 0;
        for (int i = row - 1; i >= 0; i--) {
            dp[i][col] = Math.max(dp[i + 1][col] - m[i][col], 1);
            for (int j = col - 1; j >= 0; j--) {
                right = Math.max(dp[i][j + 1] - m[i][j], 1);
                down = Math.max(dp[i + 1][j] - m[i][j], 1);
                dp[i][j] = Math.min(right, down);
            }
        }
        return dp[0][0];
    }

    public static void main(String[] args) {
        int[][] map = {
                {-2, -3, 3},
                {-5, -10, 1},
                {10, 30, -5},
        };
        System.out.println(minHP(map));
        System.out.println(minHP1(map));
    }

}