题意:有一个矩阵,要求从左上角走到右下角。每个方格里有正数或者负数。从左上角走的时候有一定的血量,走到一个方格,若该方格是负数,则需要减去该方格的血量,若是正数,则可以加上该方格的血量。若血量小于等于0,则不能继续走下去。求若想走到右下角,则从左上角出发时最少需要多少的血量。
链接:https://cn.vjudge.net/contest/183544#status//E/1/
//转移方程:该点的最终答案+a[i][j] = 其右方或下方的最终答案
动态规划的意义就在于如果更换问法,其过程中的每一步都可以将其当成最终答案
例如本例中的b[i][j]每一步都是到达该点的最终答案,本题直接跑最短路并不是最优解
在分解为每一步时,抛去b初始化为inf的情况(因为会被min舍弃掉),自下而上的动态规划过程中
每个要计算的点其下方和右方都是已经得到的该问题的缩小版的最优结果(除了下方或右方是边界)
此时对于该点来说,该点的最终答案+a[i][j] = 其右方或下方的最终答案
当计算出来的 其右方或下方的最终答案-a[i][j] 小于等于零时,说明在该点的值不需要再加任何数
就能完成这一步,但是由于题目中的要求至少该点的值为1,所以max(1,答案)为b[i][j]
当 其右方或下方的最终答案-a[i][j] 大于零时 说明在该点需要获得某个数才能够完成这一步,
又由于题目中求的是最小值,所以b[i][j]取下方或右方中的较小值
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
using namespace std;
const int inf = 0x3f3f3f3f;
int a[600][600],b[600][600];
int main()
{
int t;
cin>>t;
int n,m;
while(t--)
{
cin>>n>>m;
memset(b,inf,sizeof(b));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
}
}
//转移方程:该点的最终答案+a[i][j] = 其右方或下方的最终答案
//动态规划的意义就在于如果更换问法,其过程中的每一步都可以将其当成最终答案
//例如本例中的b[i][j]每一步都是到达该点的最终答案,本题直接跑最短路并不是最优解
//在分解为每一步时,抛去b初始化为inf的情况(因为会被min舍弃掉),自下而上的动态规划过程中
//每个要计算的点其下方和右方都是已经得到的该问题的缩小版的最优结果(除了下方或右方是边界)
//此时对于该点来说,该点的最终答案+a[i][j] = 其右方或下方的最终答案
//,当计算出来的 其右方或下方的最终答案-a[i][j] 小于等于零时,说明在该点的值不需要再加任何数
//就能完成这一步,但是由于题目中的要求至少该点的值为1,所以max(1,答案)为b[i][j]
//当其右方或下方的最终答案-a[i][j] 大于零时 说明在该点需要获得某个数才能够完成这一步,
//又由于题目中求的是最小值,所以b[i][j]取下方或右方中的较小值
for(int i=n;i>=1;i--)
{
for(int j=m;j>=1;j--)
{
if(i==n&&j==m)
{
b[n][m] = 1;
}
else
{
b[i][j] = max(1,min(b[i+1][j]-a[i][j],b[i][j+1]-a[i][j]));
}
}
}
cout<<b[1][1]<<endl;
}
return 0;
}
法2:
标程 二分+dp
枚举找出最小的生命值
对判断是否满足题意的时候,如果其上方或左方生命值已经小于1,那则从该条路便走不通了