完美世界-2019春招第二道算法题--圣斗士救雅典娜
一、题目介绍:圣斗士救雅典娜
* 题目介绍:完美世界-第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春招第二道算法题--圣斗士救雅典娜相关推荐
- 字节跳动2019春招第二次笔试编程题
字节跳动2019春招第二次笔试编程题 1.变身程序员 题目描述 输入描述 输出描述 示例 示例1 示例2 示例3 分析 参考代码 2.特征提取 题目描述 输入描述 输出描述 示例 示例1 备注 分析 ...
- 2023年春招热门笔试算法题(C++)
内容整理自网络,侵权联系删除 1.链表反转 输入:{1,2,3} 返回值:{3,2,1} struct ListNode {int val;struct ListNode *next;ListNode ...
- [C语言]字节跳动2019春招研发部分编程题
1.万万没想到之聪明的编辑 题目描述: 我叫王大锤,是一家出版社的编辑.我负责校对投稿来的英文稿件,这份工作非常烦人,因为每天都要去修正无数的拼写错误.但是,优秀的人总能在平凡的工作中发现真理.我发现 ...
- 【牛客网】字节跳动2019春招研发部分编程题汇总
1-万万没想到之聪明的编辑 解题思路 此题的编辑规则如下 三个同样的字母连在一起,一定是拼写错误,去掉一个的就好啦:比如 helllo -> hello 两对一样的字母(AABB型)连在一起,一 ...
- 字节跳动2019春招研发部分编程题汇总(Python版本)
一.万万没想到之聪明的编辑 王大锤是一家出版社的编辑,负责校对投稿来的英文稿件,他发现一个发现拼写错误的捷径: 三个同样的字母连在一起,一定是拼写错误,去掉一个的就好啦:比如 helllo -> ...
- 字节跳动2019春招研发部分编程题汇总【题解】
差不多2个小时才AK,题目难度还行吧. 自己好菜. 题目地址:https://www.nowcoder.com/test/16516564/summary 目录 万万没想到之聪明的编辑 [模拟] 万万 ...
- 字节跳动2019春招研发部分编程题汇总
一:万万没想到之聪明的编辑 题目描述 给定一个字符串,按照要求修改字符串,输出最后的结果 1. 三个同样的字母连在一起,一定是拼写错误,去掉一个的就好啦:比如 helllo -> hello 2 ...
- 【公司真题--字节跳动】字节跳动2019春招研发部分编程题汇总
文章目录 1.万万没想到之聪明的编辑 2.万万没想到之抓捕孔连顺 解法一:回溯(超时) 解法二:确定区间后数学组合问题直接求解 3.雀魂启动 1.万万没想到之聪明的编辑 直接暴力了! #include ...
- 字节跳动2019春招研发部分编程题_N个建筑中选定3个埋伏地点_决定相距最远的两名特工间的距离不超过D_可行的埋伏方案
题目 import java.util.Scanner;public class Main_N_D_Second_2 {private static int mod = 99997867;public ...
最新文章
- 【Java并发编程】面试必备之线程池
- threejs模型可视化编辑器_一个近乎无门槛、零基础的3D场景编辑器
- replugin源码解析之replugin-host-gradle(宿主的gradle插件)
- 腾讯2019暑期实习生提前批CV岗笔试题
- edittext怎么输入默认内容覆盖_Linux Shell 输入与输出重定向
- 会导致所有者权益减少的项目是_第二章:会计要素的确认(11)所有者权益
- C++ 枚举类型详解
- linux下svn常用指令
- [有限元]虚位移原理和虚力原理的证明的统一逻辑
- 前端面试题汇总(JS 基础篇)
- get_mempolicy(2) /set_mempolicy(2)/mbind(2)/numa(3) — Linux manual page
- 二叉线索树的线索化以及遍历
- ajax 实时进度_【乐建工程宝】如何把控施工项目进度
- 企业级应用与互联网应用的区别?
- 个人计算机预防勒索病毒,避免电脑中勒索病毒的方法
- 开端-从c语言入门到不放弃
- 可以直接复制的emoji符号(表情)
- 大一计算机课程ppt作业,《计算机应用基础》课程第4次作业-PPT操作题答案步骤...
- mac版的PHP集成环境软件MxSrvs软件
- 网页中的字体对应的word字体大小对照表
热门文章
- ssh-keygen免密登录详解
- 靠AI六小时开发出游戏Demo,剧本绘画配音一条龙,网友:新概念3“A”大作
- 没有steam土耳其支付方式怎么充值?
- c++builder实现文件拖拽与U盘复制---YYB原创(转载请注明出处)
- Vijos1767-YYB喋血
- ffmpeg ios无声音 视频合成声音没有声音
- 使用python+Tkinter库构建GUI应用工具 - 个性签名工具
- java输出斜塔_在Windows 7的开始菜单中,除了右窗格上下各有用户账户按钮和计算机关闭选项按钮外,主要有三个基本部分,而________不属于开始菜单的基本组成...
- 12种公认的减肥食品
- 3.B1019数字黑洞