左神算法 龙与地下城游戏

【题目】给定一个二维数组map,含义是一张地图,例如,如下矩阵
-2 -3 3
-5 -10 1
0 30 -5
游戏规则如下:骑士从左上角出发,每次只能向右或者向下走,最后到到右下角见到公主。
地图中每隔位置代表骑士要遭遇的事情,如果是负数,骑士损失血量,如果是非负数,能让骑士回血。
骑士从左上角到右下角的过程中,走到任何一个位置,血量都不能少于1。

动态规划解法
下面想的是错误的解法,把问题想简单了。这个做法从左上到坐下相当于在收集能量,如果求骑士能收集的最多能量是多少,就用这个方法。

public static int minHP(int[][] m){
        if (m == null || m.length == 0 || m[0] == null || m[0].length == 0){
            return 0;
        }
        int[][] dp = new int[m.length][m[0].length];
        dp[0][0] = m[0][0];
        for (int i = 1; i < m[0].length; i++) {
            dp[0][i] = dp[0][i-1] + m[0][i];
        }
        for (int i = 1; i <m.length; i++) {
            dp[i][0] = dp[i-1][0] + m[i][0];
        }
        for (int i = 1; i <m.length ; i++) {
            for (int j = 1; j <m[0].length ; j++) {
                dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]) +m[i][j];
            }
        }
        return dp[m.length-1][m[0].length-1] ;
        //return dp[m.length-1][m[0].length-1] > 0? 0: -dp[m.length -1][m[0].length-1];
    }

这里要保证骑士每个位置上的血量都不少于1,如果还是从开始位置出发,递推的话发现会有问题。
如果此处dp[i][j]表示要到达位置(i,j)从坐上角出发至少需要多少能量,不能从dp[i-1][j]和dp[i][j-1]推导出dp[i][j],因为如果map[i][j]处是个负数,那我还需要知道走到map[i-1][j]时还有多少能量,如果能量和该处值之和大于1,说明不用增加需要的能量。
所以此处应该从右下角开始往左上角推,右下角的值是-5,由此推出这里的值起码是6。然后推得最后一行和最后一列的值。
然后从右往左,从下往上递推,dp[i][j]表示从位置(i,j)走到右下角,至少需要多少血量。它是从dp[i+1][j]和dp[i][j+1]演化过来,取它们的较小值记做min,说明走(i,j)之后,知道要有min血量。这时就用min-map[i][j],如果值大于等于1,说明此处还需要血量这么多,如果血量小于1,说明此处是不需要血量的,但是每一个都需要血量不少于1,这时血量就是1;

 public static int minHP1(int[][] m){
        if (m == null || m.length == 0 || m[0] == null || m[0].length == 0){
            return 0;
        }
        int row = m.length;
        int col = m[0].length;
        int[][] dp = new int[row][col];
        dp[row-1][col-1] = m[row-1][col-1] >= 0? 1: -m[m.length-1][m[0].length-1] +1;
        for (int i = col -2; i >= 0; i--) {
            dp[row-1][i] = (dp[row-1][i+1] - m[row-1][i]) <= 1? 1 : dp[row-1][i+1] - m[row-1][i];
        }
        for (int i = row -2; i >= 0; i--) {
            dp[i][col-1] = (dp[i+1][col-1] - m[i][col -1]) <= 1? 1: dp[i+1][col-1] - m[i][col -1];
        }
        for (int i = row-2; i >= 0 ; i--) {
            for (int j = col -2; j >= 0 ; j--) {
                dp[i][j] = (Math.min(dp[i+1][j], dp[i][j+1]) - m[i][j]) <= 0 ? 1: Math.min(dp[i+1][j], dp[i][j+1]) - m[i][j];
            }
        }
        return dp[0][0];
    }

得出的dp数组如下
7 5 2
6 11 5
1 1 6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值