题意:在一个n*m的网格上,从(0,0)走到(n-1,m-1),每次只能向右或者向下走一格。一个人最初有一个生命值x,走到每一个格生命值会 变为x + s[i][j],(s[i][j]可为负,0,正),若生命值小于等于0,则人死亡。告诉网格上所有s[i][j],求x的最小值使得该人能够或者走到 (n-1,m-1)。|s[i][j]| < 1000,n,m < 500。
解法:这道题不能直接dp,否则会错。必须要先二分x的值,然后再dp。dp[i][j]记录的是走到(i,j)格所能有的最大生命值,但是要注意,d[i][j]只能在d[i][j-1]或d[i-1][j]中有一个为正时才能转移过来。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int dp[1000][1000]; int sum[1000][1000]; const int inf=999999999; int main(){ int t; scanf("%d",&t); while(t--){ memset(dp,0,sizeof(dp)); for(int i=0;i<=999;i++) for(int j=0;j<=999;j++) sum[i][j]=inf; int n,m; scanf("%d%d",&n,&m); int ans=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++) scanf("%d",&dp[i][j]); } sum[n][m]=1; for(int i=n;i>=1;i--){ for(int j=m;j>=1;j--){ sum[i-1][j]=min(sum[i-1][j],sum[i][j]-dp[i-1][j]); if(sum[i-1][j]<=0) sum[i-1][j]=1; sum[i][j-1]=min(sum[i][j-1],sum[i][j]-dp[i][j-1]); if(sum[i][j-1]<=0) sum[i][j-1]=1; /* if(i==1){ dp[i][j]+=dp[i][j-1]; if(dp[i][j]<=0){ sum[i+j]=min(sum[i+j],-dp[i][j]+1); dp[i][j]=1; } } else if(j==1){ dp[i][j]+=dp[i-1][j]; if(dp[i][j]<=0){ sum[i+j]=min(sum[i+j],-dp[i][j]+1); dp[i][j]=1; } } else{ dp[i][j]+=max(dp[i-1][j],dp[i][j-1]); */ } } printf("%d ",sum[1][1]); } return 0; }