一、题目介绍:圣斗士救雅典娜

* 题目介绍:完美世界-第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注释:这里简化了控制台输入的部分。

二、思路分析

* 思路分析:
* 圣斗士是从左上角往右下角走,保证每一个位置(包括终点右下角)的血量都至少为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]即可。

三、Java代码实现

public class AthenaSaint {

/**
     * 方法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();
    }
    */
}

完美世界-2019春招第二道算法题--圣斗士救雅典娜相关推荐

  1. 字节跳动2019春招第二次笔试编程题

    字节跳动2019春招第二次笔试编程题 1.变身程序员 题目描述 输入描述 输出描述 示例 示例1 示例2 示例3 分析 参考代码 2.特征提取 题目描述 输入描述 输出描述 示例 示例1 备注 分析 ...

  2. 2023年春招热门笔试算法题(C++)

    内容整理自网络,侵权联系删除 1.链表反转 输入:{1,2,3} 返回值:{3,2,1} struct ListNode {int val;struct ListNode *next;ListNode ...

  3. [C语言]字节跳动2019春招研发部分编程题

    1.万万没想到之聪明的编辑 题目描述: 我叫王大锤,是一家出版社的编辑.我负责校对投稿来的英文稿件,这份工作非常烦人,因为每天都要去修正无数的拼写错误.但是,优秀的人总能在平凡的工作中发现真理.我发现 ...

  4. 【牛客网】字节跳动2019春招研发部分编程题汇总

    1-万万没想到之聪明的编辑 解题思路 此题的编辑规则如下 三个同样的字母连在一起,一定是拼写错误,去掉一个的就好啦:比如 helllo -> hello 两对一样的字母(AABB型)连在一起,一 ...

  5. 字节跳动2019春招研发部分编程题汇总(Python版本)

    一.万万没想到之聪明的编辑 王大锤是一家出版社的编辑,负责校对投稿来的英文稿件,他发现一个发现拼写错误的捷径: 三个同样的字母连在一起,一定是拼写错误,去掉一个的就好啦:比如 helllo -> ...

  6. 字节跳动2019春招研发部分编程题汇总【题解】

    差不多2个小时才AK,题目难度还行吧. 自己好菜. 题目地址:https://www.nowcoder.com/test/16516564/summary 目录 万万没想到之聪明的编辑 [模拟] 万万 ...

  7. 字节跳动2019春招研发部分编程题汇总

    一:万万没想到之聪明的编辑 题目描述 给定一个字符串,按照要求修改字符串,输出最后的结果 1. 三个同样的字母连在一起,一定是拼写错误,去掉一个的就好啦:比如 helllo -> hello 2 ...

  8. 【公司真题--字节跳动】字节跳动2019春招研发部分编程题汇总

    文章目录 1.万万没想到之聪明的编辑 2.万万没想到之抓捕孔连顺 解法一:回溯(超时) 解法二:确定区间后数学组合问题直接求解 3.雀魂启动 1.万万没想到之聪明的编辑 直接暴力了! #include ...

  9. 字节跳动2019春招研发部分编程题_N个建筑中选定3个埋伏地点_决定相距最远的两名特工间的距离不超过D_可行的埋伏方案

    题目 import java.util.Scanner;public class Main_N_D_Second_2 {private static int mod = 99997867;public ...

最新文章

  1. 【Java并发编程】面试必备之线程池
  2. threejs模型可视化编辑器_一个近乎无门槛、零基础的3D场景编辑器
  3. replugin源码解析之replugin-host-gradle(宿主的gradle插件)
  4. 腾讯2019暑期实习生提前批CV岗笔试题
  5. edittext怎么输入默认内容覆盖_Linux Shell 输入与输出重定向
  6. 会导致所有者权益减少的项目是_第二章:会计要素的确认(11)所有者权益
  7. C++ 枚举类型详解
  8. linux下svn常用指令
  9. [有限元]虚位移原理和虚力原理的证明的统一逻辑
  10. 前端面试题汇总(JS 基础篇)
  11. get_mempolicy(2) /set_mempolicy(2)/mbind(2)/numa(3) — Linux manual page
  12. 二叉线索树的线索化以及遍历
  13. ajax 实时进度_【乐建工程宝】如何把控施工项目进度
  14. 企业级应用与互联网应用的区别?
  15. 个人计算机预防勒索病毒,避免电脑中勒索病毒的方法
  16. 开端-从c语言入门到不放弃
  17. 可以直接复制的emoji符号(表情)
  18. 大一计算机课程ppt作业,《计算机应用基础》课程第4次作业-PPT操作题答案步骤...
  19. mac版的PHP集成环境软件MxSrvs软件
  20. 网页中的字体对应的word字体大小对照表

热门文章

  1. ssh-keygen免密登录详解
  2. 靠AI六小时开发出游戏Demo,剧本绘画配音一条龙,网友:新概念3“A”大作
  3. 没有steam土耳其支付方式怎么充值?
  4. c++builder实现文件拖拽与U盘复制---YYB原创(转载请注明出处)
  5. Vijos1767-YYB喋血
  6. ffmpeg ios无声音 视频合成声音没有声音
  7. 使用python+Tkinter库构建GUI应用工具 - 个性签名工具
  8. java输出斜塔_在Windows 7的开始菜单中,除了右窗格上下各有用户账户按钮和计算机关闭选项按钮外,主要有三个基本部分,而________不属于开始菜单的基本组成...
  9. 12种公认的减肥食品
  10. 3.B1019数字黑洞