dp问题描述
地下城游戏

请你看上面示例一中的标准答案那条路线,以及下下右右这条路线。非常显然,当骑士走到最后的时候,第二条路线的剩余体力是最多的。但是实际上第一条路线和第二条路线对于骑士来说是等价的,因为在这两条路线行走的过程中,骑士的体力相对于他进迷宫之前最多下降7点体力,因此我们要求的并不是骑士走完这条路线的体力剩余,而是骑士在这条路线中体力最多降低多少。
确定本题的状态表示
本题倒着做是比较方便的,我一开始正着做感觉还是很折磨的(因为这样做是有后效性的,一个节点的取值不仅受到左边和上边的格子影响,还可能受到右边和下边的格子的影响),最后47个测试用例过了46个,最后一个真没办法,因为如果把最后一个改对了,我一运行,就只能过36个了。
言归正传,在本题中我们规定:dp [i][j] 表示从 [i, j] 位置出发,到达终点,所需的最低初始健康点数
确定本题的状态转移方程

在理解这个运行逻辑的时候,建议举一个最简单的例子。
vector<vector<int>>& dungeon={{0,-5}}
在这个例子中,我们要想让骑士活着救出公主,那么他的体力值在进入地下城之前应该是6点,初始情况下我们会令dp[1][1]=1,那么要计算dp[1][2]就应该拿1-(-5)=6,他表示的含义就是我想要在走出迷宫的时候,体力值大于等于一,那我在进入最后一格之前,我的体力值就应该大于等于 6。
填表求值
根据初始条件和状态转移方程,确定填表顺序,进而逐步填满dp表,最终返回题目要的结果
代码实现
class Solution {
public:
int calculateMinimumHP(vector<vector<int>>& d) {
// 1. 创建 dp 表
// 2. 初始化
// 3. 填表
// 4. 返回值
int m = d.size(), n = d[0].size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, INT_MAX));
dp[m][n - 1] = dp[m - 1][n] = 1;
for(int i = m - 1; i >= 0; i--)
for(int j = n - 1; j >= 0; j--)
{
dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - d[i][j];
dp[i][j] = max(1, dp[i][j]);
}
return dp[0][0];
}
};
下面附上我一个小时44/45的丑陋代码,虽然拼尽全力无法战胜,但我觉得应该给我那逝去的一个小时做一个纪念
class Solution {
public:
int calculateMinimumHP(vector<vector<int>>& dungeon) {
int m=dungeon.size();
if(m==0) return 0;
int n=dungeon[0].size();
vector<vector<int>> dp(m+1,vector<int>(n+1,0));
vector<vector<int>> min_differ(m+1,vector<int>(n+1,0));
cout << m << n<< endl;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
cout << "i="<<i<<" j=" << j<< endl;
if(i==1){
dp[i][j]=dp[i][j-1]+dungeon[i-1][j-1];
min_differ[i][j]=min(min_differ[i][j-1],dp[i][j]);
}
else if(j==1) {
dp[i][j]=dp[i-1][j]+dungeon[i-1][j-1];
min_differ[i][j]=min(min_differ[i-1][j],dp[i][j]);
}
else {
dp[i][j]=max(dp[i][j-1],dp[i-1][j])+dungeon[i-1][j-1];
int left_dp=dp[i][j-1]+dungeon[i-1][j-1];
int right_dp=dp[i-1][j]+dungeon[i-1][j-1];
int left=min(min_differ[i][j-1],left_dp);
int up=min(min_differ[i-1][j],right_dp);
min_differ[i][j]=max(left,up);
}
cout << "dp["<<i<<"]["<<j<<"]="<<dp[i][j];
cout << " min_differ["<<i<<"]["<<j<<"]="<<min_differ[i][j]<< endl;
}
}
return 1-min_differ[m][n];
}
};
6864

被折叠的 条评论
为什么被折叠?



