题意简述:有一个M*N的网格迷宫,位于左上角的骑士要到达右下角的公主的位置。每到达一个位置,骑士的生命值都会发生变化(负数为减少,正数为增加),如果在途中骑士的生命值降至0或以下,会立刻死亡。
假设骑士的生命值没有上限,而且每一步只能向右或者向下走。给定一个迷宫,那么骑士要安全到达右下角最少需要的初始生命值是多少。
输入:嵌套vector,代表迷宫每个网格骑士生命值的变化。
输出:一个int,代表最少需要的初始生命值。
题解:
对于每个网格只有两种选择,因此可以从终点右下角开始,向左向上递推出每个网格到达终点最少需要的初始生命值是多少,递推到起点左上角即可得到结果。
递推过程如下:
- 右下角是递推的起始,只需要保证生命值加上网格的值大于等于1即可,如果网格的值为正,生命值1就可以;如果为负,需要加上网格的值的相反数。用公式表示就是:
dp(m−1,n−1)=max(1−dungeon(m−1,n−1), 1)
- 对于最下边的一行,每个网格都只有向右走这一选择,由于右边的网格已经计算出最少需要的生命值,因此只有当前网格以及它右边的网格两个数值参与计算。
dp(m−1,j)=max(dp(m−1,j+1)−dungeon(m−1,j), 1)(j=n−2,n−3,...,0)
- 对于最右边的一列,每个网格都只有向下走这一选择,由于下边的网格已经计算出最少需要的生命值,因此只有当前网格以及它下边的网格两个数值参与计算。
dp(i,n−1)=max(dp(i+1,n−1)−dungeon(i,n−1), 1)(i=m−2,m−3,...,0)
- 对于其他网格,都有两种选择,取生命值更少的选择。
dp(i,j)=max(min(dp(i+1,j),dp(i,j+1))−dungeon(i,j), 1)
算法的时间复杂度和空间复杂度均为O(M*N)。
class Solution {
public:
int calculateMinimumHP(vector<vector<int>>& dungeon) {
int m = dungeon.size(), n = dungeon[0].size();
vector<vector<int>> dp;
for(int i = 0;i < dungeon.size();i++) {
vector<int> temp;
for(int j = 0; j < dungeon[0].size();j++)
temp.push_back(0);
dp.push_back(temp);
}
// border
dp[m-1][n-1] = max(1-dungeon[m-1][n-1],1);
for(int i = n-2;i >=0;i--)
dp[m-1][i] = max(-dungeon[m-1][i] + dp[m-1][i+1],1);
for(int i = m-2;i >=0;i--)
dp[i][n-1] = max(-dungeon[i][n-1] + dp[i+1][n-1],1);
// dp
for(int i = m -2;i >= 0;i--) {
for(int j = n -2;j >=0;j--) {
dp[i][j] = max(-dungeon[i][j] + min(dp[i][j+1],dp[i+1][j]),1);
}
}
return dp[0][0];
}
};