完美世界-第2道算法题目-黄金圣斗士欧洛斯要去圣域救雅典娜

这道题与Leetcode的第62题目类似,有兴趣的同学可以查看下:都属于动态规划的题目。
https://www.nowcoder.com/discuss/188537
* 题目介绍:完美世界-第2道算法题目
     * 黄金圣斗士欧洛斯要去圣域救雅典娜,需要从左上角出发,每次只能向右或向下走,最后达到右下角见到雅典娜。
     * 地图的每个位置的值代表圣斗士要遭遇的事情,如果是负数,说明此处有阻击,要让圣斗士损失血量,如果是非负数,
     * 代表次数有血瓶,能让圣斗士回血。圣斗士从左上角走到右下角的过程中,走到任何一个位置时,血量都不少于1,
     * 为了保证圣斗士能救出雅典娜,初始血量至少是多少?地图为一个二维数组map,如下矩阵。根据map,返回初始血量。
     * -2  -3 3
     * -5 -10 1
     * 0 30 -5
     * 初始血量至少为7。
     * 输入描述:一个n*m的二维数组。第一行:数组的行数n(n>0);第二行:数组的列数m(m>0);
     * 第三行:数组,每个位置的血量,行优先。
     * 输出描述:对于每个测试实例,要求输出初始血量。
     * 示例:
     * 输入:
     * 3
     * 3
     * -2 -3 3 -5 -10 1 0 30 -5
     * 输出:
     * 7
     * <p>
     * 特殊测试例子:如果经过的每个位置都是加血量,则初始血量也至少为1。
     * 思路分析:
     * 圣斗士是从左上角往右下角走,保证每一个位置(包括终点右下角)的血量都至少为1。
     * 欲求起始的最少血量,因此需要逆着推,从终点向起始点推导,且保证每个位置的血量至少为1。
     * 方法1:动态规划方法。
     * S1: 首先初始化:dp[rows - 1][cols - 1]、最后一列和最后一行。
     *      dp[rows-1][j]=max{(dp[rows-1][j+1]-blood[i][j]),1};
     *      dp[i][cols-1]=max{dp[i+1][cols-1]-blood[i][cols-1],1};
     * S2:计算其他位置
     *      dp[i][j] = max{(min{dp[i][j+1],dp[i+1][j]}-blood[i][j]),1};
     *      最后返回dp[0][0]即可。
     * 注释:max{1,x}的目的是保证到达该位置之前血量至少为1。
     * 方法2:递归方法。
     *  仍然利用dp数组保存到达该位置前需要的至少血量。
     *  递归方法:完成dp[i][j]的更新,并递归下一步。
     *  递归终止条件:①出了数组边界;②当前计算的血量不是最少的。
     *  实现:从(rows-1,cols-1)开始倒着推导,更新完当前至少血量后,后续有两种走法:向上或向左;
     *  运行完之后,所有位置的dp都被更新,返回dp[0][0]即可。
     */

    /**
     * 方法1: 动态规划
     */
    public int needMinBlood_DP(int[][] bloods) {
        if (bloods == null || bloods.length == 0) return 1;
        int rows = bloods.length, cols = bloods[0].length;
        //创建一个dp数组,dp[i][j]表示走到该位置需要的最少血量
        int[][] dp = new int[rows][cols];
        //S1:最后一列和最后一行dp初始化
        dp[rows - 1][cols - 1] = Math.max(1, 1 - bloods[rows - 1][cols - 1]);
        for (int i = rows - 2; i >= 0; i--)//对最后一列初始化
            dp[i][cols - 1] = Math.max(1, dp[i + 1][cols - 1] - bloods[i][cols - 1]);
        for (int j = cols - 2; j >= 0; j--)//对最后一行初始化
            dp[rows - 1][j] = Math.max(1, dp[rows - 1][j + 1] - bloods[rows - 1][j]);
        //S2:计算其他位置dp
        for (int i = rows - 2; i >= 0; i--) {
            for (int j = cols - 2; j >= 0; j--) {
                dp[i][j] = Math.max(1, Math.min(dp[i][j + 1], dp[i + 1][j]) - bloods[i][j]);
            }
        }
        printArray("各个位置需的最少的血量:", dp);
        return dp[0][0];
    }

    /**
     * 方法2:递归方法
     */
    public int needMinBlood(int[][] bloods) {
        if (bloods == null || bloods.length == 0) return 1;
        int rows = bloods.length, cols = bloods[0].length;
        //创建一个dp数组,到达该位置需要的最少血量,初始化为Integer的最大值
        int[][] needMinBloodDp = new int[rows][cols];
        for (int i = 0; i < rows; i++)
            for (int j = 0; j < cols; j++)
                needMinBloodDp[i][j] = Integer.MAX_VALUE;
        //从右下角开始,remainBlood=1表示走完该(row,col)位置需要剩余的血量
        recursive(bloods, needMinBloodDp, rows - 1, cols - 1, 1);
        printArray("各个位置需的最少的血量:", needMinBloodDp);
        return needMinBloodDp[0][0];
    }

    /**
     * 递归方法
     */
    private void recursive(int[][] nums, int[][] needMinBloodDp, int row, int col, int remainBlood) {
        if (row < 0 || col < 0 || row >= nums.length || col >= nums[0].length
                || remainBlood - nums[row][col] >= needMinBloodDp[row][col])
            return;//边界条件+当前需要血量不是最低的,不更新,直接退出
        needMinBloodDp[row][col] = Math.max(1, remainBlood - nums[row][col]);//当前位置可以需要更少的血量,且需要继续往下更新
        //下一步:要么向上走,要么向左走
        recursive(nums, needMinBloodDp, row - 1, col, needMinBloodDp[row][col]);
        recursive(nums, needMinBloodDp, row, col - 1, needMinBloodDp[row][col]);
    }

    /**
     * 辅助分析结果的方法:打印二维数组
     */
    private static void printArray(String title, int[][] nums) {
        if (nums == null || nums.length == 0) return;
        int rows = nums.length;
        int cols = nums[0].length;
        System.out.println(title);
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                System.out.print(String.format("%2d;", nums[i][j]));
            }
            System.out.println();
        }
    }

    /***********************测试***************************/
    @Test
    public void test() {
        int[][] bloods1 = {{-2, -3, 3}, {-5, -10, 1}, {0, 30, -5}};//7
        int[][] bloods2 = {{-2, -5, 3}, {-5, -10, 1}, {0, 30, -5}};//8
        int[][] bloods3 = {{-2, -5, 3}, {-5, -10, 1}, {0, -30, -5}};
        int[][] bloods4 = {{2, 5, 3}, {5, -10, 1}, {0, 30, -5}};
        int[][] bloods = bloods4;
        printArray("原始的血量数组:", bloods);
        System.out.println("--------------动态规划方法---------------");
        System.out.println(String.format("需要的最少的血量:%3d", needMinBlood(bloods)));
        System.out.println("--------------递归方法---------------");
        System.out.println(String.format("需要的最少的血量:%3d", needMinBlood_DP(bloods)));
    }


    /*
         //根据scanner输入转化为二维数组
    public static int[][] sccanerInTobloods() {
        Scanner scanner = new Scanner(System.in);
        int rows = Integer.valueOf(scanner.nextLine());
        int cols = Integer.valueOf(scanner.nextLine());
        String line = scanner.nextLine();
        String[] numStrs = line.split(" ");
        int[][] nums = new int[rows][cols];
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                nums[i][j] = Integer.parseInt(numStrs[i * rows + j]);
            }
        }
        return nums;
    }

    //测试专用
    public static void main(String[] args) {
        //注释:在非main方法中调用Scanner.nextLine是无效的。
        int[][] bloods = sccanerInTobloods();
    }
    */
}

#完美世界##笔试题目##题解#
全部评论
  /**      * 方法1: 动态规划      */     public int needMinBlood_DP(int[][] bloods) {         if (bloods == null || bloods.length == 0) return 1;         int rows = bloods.length, cols = bloods[0].length;         //创建一个dp数组,dp[i][j]表示走到该位置需要的最少血量         int[][] dp = new int[rows][cols];         //S1:最后一列和最后一行dp初始化         dp[rows - 1][cols - 1] = Math.max(1, 1 - bloods[rows - 1][cols - 1]);         for (int i = rows - 2; i >= 0; i--)//对最后一列初始化             dp[i][cols - 1] = Math.max(1, dp[i + 1][cols - 1] - bloods[i][cols - 1]);         for (int j = cols - 2; j >= 0; j--)//对最后一行初始化             dp[rows - 1][j] = Math.max(1, dp[rows - 1][j + 1] - bloods[rows - 1][j]);         //S2:计算其他位置dp         for (int i = rows - 2; i >= 0; i--) {             for (int j = cols - 2; j >= 0; j--) {                 dp[i][j] = Math.max(1, Math.min(dp[i][j + 1], dp[i + 1][j]) - bloods[i][j]);             }         }         return dp[0][0];     }
点赞 回复 分享
发布于 2019-04-04 13:25
 /**      * 方法2:递归方法      */     public int needMinBlood(int[][] bloods) {         if (bloods == null || bloods.length == 0) return 1;         int rows = bloods.length, cols = bloods[0].length;         //创建一个dp数组,到达该位置需要的最少血量,初始化为Integer的最大值         int[][] needMinBloodDp = new int[rows][cols];         for (int i = 0; i < rows; i++)             for (int j = 0; j < cols; j++)                 needMinBloodDp[i][j] = Integer.MAX_VALUE;         //从右下角开始,remainBlood=1表示走完该(row,col)位置需要剩余的血量         recursive(bloods, needMinBloodDp, rows - 1, cols - 1, 1);         return needMinBloodDp[0][0];     }     /**      * 递归方法      */     private void recursive(int[][] nums, int[][] needMinBloodDp, int row, int col, int remainBlood) {         if (row < 0 || col < 0 || row >= nums.length || col >= nums[0].length                 || remainBlood - nums[row][col] >= needMinBloodDp[row][col])             return;//边界条件+当前需要血量不是最低的,不更新,直接退出         needMinBloodDp[row][col] = Math.max(1, remainBlood - nums[row][col]);//当前位置可以需要更少的血量,且需要继续往下更新         //下一步:要么向上走,要么向左走         recursive(nums, needMinBloodDp, row - 1, col, needMinBloodDp[row][col]);         recursive(nums, needMinBloodDp, row, col - 1, needMinBloodDp[row][col]);     }
点赞 回复 分享
发布于 2019-04-04 13:25
现在刚做完完美世界的笔试,完美世界是不是不想找了啊,咋还出一样的题目,早知道查一查省的写了。。。
点赞 回复 分享
发布于 2019-04-15 20:56
**考上次一样的题
点赞 回复 分享
发布于 2019-04-15 21:09

相关推荐

评论
点赞
12
分享

创作者周榜

更多
牛客网
牛客企业服务