题意
一个2D迷宫,公主在(m, n)的位置,骑士在(0, 0)的位置,并且有一个初始生命值。
现在骑士要去(m, n)的位置,只能向右或者向上走。每个格子的位置上,都有BUFF和DEBUFF,现在要保持骑士在任何时刻的生命值都要大于0,求骑士的初始状态的最小生命值。
思路
算法1
二分+dp。这是一个比较直观的思路,我们二分一下骑士初始的生命值,然后dp去判断是否合法。
状态表示: d[i,j] ,在位置[i, j]上的生命值。
转移方程: d[i,j]=max(d[i,j−1],d[i−1,j])+a[i,j] 。且 d[i,j−1] 和 d[i−1,j] 至少存在一个大于0。
目标状态: d[m,n]>0
算法2
直接dp。我们从(m, n)的位置开始倒推(0, 0)位置的最小生命值。
转移方程: d[i,j]=max(1,min(d[i+1,j],d[i,j+1])−a[i,j])
代码
//algorithm 1
const int maxn = 1005;
#define INF 0x3e3e3e3e
class Solution {
public:
int m, n;
vector<vector<int>> a;
int d[maxn][maxn];
int dfs(int i, int j, int x) {
if (d[i][j] != -1) return d[i][j];
if (!i && !j) return d[i][j] = x + a[i][j];
if (!i) return d[i][j] = dfs(i, j - 1, x) > 0 ? dfs(i, j - 1, x) + a[i][j] : -INF;
if (!j) return d[i][j] = dfs(i - 1, j, x) > 0 ? dfs(i - 1, j, x) + a[i][j] : -INF;
int t1 = dfs(i - 1, j, x);
int t2 = dfs(i, j - 1, x);
if (t1 > 0 || t2 > 0) d[i][j] = max(t1, t2) + a[i][j];
else d[i][j] = -INF;
return d[i][j];
}
bool judge(int x) {
memset(d, -1, sizeof(d));
return dfs(m - 1, n - 1, x) > 0;
}
int calculateMinimumHP(vector<vector<int>>& g) {
a = g;
m = a.size();
if (!m) return 0;
n = a[0].size();
int L = 1, R = INT_MAX, M = L + (R - L) / 2;
while (L < R) {
if (R == L + 1) {
if (judge(L)) M = L;
else M = R;
break;
} else {
M = L + (R - L) / 2;
if (judge(M)) R = M;
else L = M;
}
}
return M;
}
};
//algorithm 2
const int maxn = 1005;
#define INF 0x3e3e3e3e
class Solution {
public:
int d[maxn][maxn];
int calculateMinimumHP(vector<vector<int>>& a) {
int m = a.size();
if (!m) return 0;
int n = a[0].size();
d[m - 1][n - 1] = a[m - 1][n - 1] > 0 ? 1 : -a[m - 1][n - 1] + 1;
for (int i = m - 2; i >= 0; i--) d[i][n - 1] = max(1, d[i + 1][n - 1] - a[i][n - 1]);
for (int j = n - 2; j >= 0; j--) d[m - 1][j] = max(1, d[m - 1][j + 1] - a[m - 1][j]);
for (int i = m - 2; i >= 0; i--) {
for (int j = n - 2; j >= 0; j--) {
d[i][j] = max(1, min(d[i + 1][j], d[i][j + 1]) - a[i][j]);
}
}
return d[0][0];
}
};