题目给的条件
1. 二维网格m*n 行列 //m=1 n=1
2. P右下角
3. k左上角
4. k可以向右和向下移动
5. 遇怪掉血
6. 遇药加血
7. 血量<=0骑士会死
要求:求出救到公主的最小血量
- 骑士的健康没有上限
- 每个房子有怪物或者血瓶
第一个想法:
深度优先搜索遍历,从p开始假设救到公主只剩1滴血,
开始搜索,遇到怪加血,遇到血瓶把血压下去,最后到达k的时候更新最小血量
当遇到草鸡大的血瓶可能会把自己血量压空甚至压成负数,所以判断一下
//java
//TLE 美滋滋ლ(′◉❥◉`ლ)
class Solution {
public int calculateMinimumHP(int[][] dungeon) {
int m = dungeon.length;
int n = dungeon[0].length;
if(m==1&&n==1) {
if(dungeon[0][0]>0)
return 1;
else
return -dungeon[0][0]+1;
}
if(m==1) {//只有一行
int ans = 1;//假设救到公主的时候就1滴血了
for(int i=n-1;i>=0;i--) {//从P到K
if(ans-dungeon[0][i]>0) {
ans -= dungeon[0][i];//能捡到药包血量可以压低一点
}else {//不能压到死,死了是捡不到血包的
ans = 1;
}
}
return ans;
}
if(n==1) {//只有一列
int ans = 1;//假设救到公主的时候就1滴血了
for(int i=m-1;i>=0;i--) {//从P到K
if(ans-dungeon[i][0]>0) {
ans -= dungeon[i][0];//能捡到药包血量可以压低一点
}else {//不能压到死,死了是捡不到血包的
ans = 1;
}
}
return ans;
}
//位置,遍历过程记录中间血量,最小血量
int [] status = new int[] {Integer.MAX_VALUE};
dfs(dungeon,m-1,n-1,1,status);
// System.out.println("status>>>>>>>>>>>>>>>>>>>>>>>");
// System.out.println(status[0]);
return status[0];
}
private void dfs(int[][] dungeon,int x,int y,int now, int[] status) {
if(x<0||y<0||
x>=dungeon.length||
y>=dungeon[0].length)//出界了
return ;
if(x==0&&y==0) {
//K这个格子不要忘记
if(now-dungeon[0][0]<0) {
now = 1;
}else {
now -= dungeon[0][0];
}
if(status[0]>now) { //更新血量
status[0] = now;
}
}else {
for(int i=0;i<2;i++) {//0表示左移,1表示上移
int what = dungeon[x][y];
int temp = now;//保存未改变的血量
if(what<0) {
now -= what;
}else {
if(now-what>0) {
now -= what;//能捡到药包血量可以压低一点
}else {//不能压到死,死了是捡不到血包的
now = 1;
}
}
if(i==0)
dfs(dungeon,x,y-1,now,status);
else
dfs(dungeon,x-1,y,now,status);
now=temp;
}
}
}
}
又想了一个方法递推:
规则说了K只能向右和向下,所以公主也只能向左或向上,所以后一列和底行是直接能求出最小值,然后从左下角开始推,比较向左和向上走哪个更优,然后取最小值
//美滋滋ლ(′◉❥◉`ლ)
class Solution {
public int calculateMinimumHP(int[][] dungeon) {
int m = dungeon.length;
int n = dungeon[0].length;
if(m==1&&n==1) {
if(dungeon[0][0]>0)
return 1;
else
return -dungeon[0][0]+1;
}
if(m==1) {//只有一行
int ans = 1;//假设救到公主的时候就1滴血了
for(int i=n-1;i>=0;i--) {//从P到K
if(ans-dungeon[0][i]>0) {
ans -= dungeon[0][i];//能捡到药包血量可以压低一点
}else {//不能压到死,死了是捡不到血包的
ans = 1;
}
}
return ans;
}
if(n==1) {//只有一列
int ans = 1;//假设救到公主的时候就1滴血了
for(int i=m-1;i>=0;i--) {//从P到K
if(ans-dungeon[i][0]>0) {
ans -= dungeon[i][0];//能捡到药包血量可以压低一点
}else {//不能压到死,死了是捡不到血包的
ans = 1;
}
}
return ans;
}
//其实就用到两列数据
int[] bottom = new int[n];//底行
bottom[n-1] = 1;
if(bottom[n-1]-dungeon[m-1][n-1]>0) {
bottom[n-1] -= dungeon[m-1][n-1];
}else {
bottom[n-1] = 1;
}
for(int i=bottom.length-2;i>=0;i--) {//底行
if(bottom[i+1]-dungeon[m-1][i]>0) {
bottom[i] = bottom[i+1]-dungeon[m-1][i];
}else {
bottom[i] = 1;
}
}
//最左侧一列
int[] col = new int[m-1];
if(bottom[n-1]-dungeon[m-2][n-1]>0) {
col[col.length-1] = bottom[n-1]-dungeon[m-2][n-1];
}else {
col[col.length-1] = 1;
}
for(int i=col.length-2;i>=0;i--) {
if(col[i+1]-dungeon[i][n-1]>0) {
col[i] = col[i+1]-dungeon[i][n-1];
}else {
col[i] = 1;
}
}
// 开始推:
for(int i=n-2;i>=0;i--) {//列
for(int j=m-2;j>=0;j--) {//行
//向左
int left = col[j];
if(left-dungeon[j][i]>0) {
left = left-dungeon[j][i];
}else {
left = 1;
}
//向上
int top = bottom[i];
if(top-dungeon[j][i]>0) {
top = top-dungeon[j][i];
}else {
top = 1;
}
col[j] = Math.min(left,top);
bottom[i] = col[j];
}
}
return col[0];
}
}