题目链接:https://www.spoj.com/problems/AMR11A/en/。
题意:给一个m*n的网格,网格的左上角及右下角为零,每次只能往下走或者往右走一个格子同时不能走出该网格,每次走加上该网格的值。问:从左上角走到右下角,在这个过程中不能使总值小于零,输出最小的初始值(即在左上角的初始值)。
思路:逆向dp,求出初始值。
设dp【i】【j】为到达(i,j)前所剩下的最小值。(就比如dp【m】【n】=1,因为你到达(m,n)的时候加上该格子的值mp【m】【n】(0)要大于零)。
从终点往前推:首先找到正向行走时的下一个格子min(dp【i】【j+1】,dp【i+1】【j】),然后减掉走到(i,j)所加的值mp【i】【j】,就是dp【i】【j】的值了,当然这和值必须大于零,所以可以得到状态方程:dp【i】【j】=max(min(dp【i】【j+1】,dp【i+1】【j】)-mp【i】【j】,1)。当然还得分情况:
到达最后一行时只能往右走所以dp【m】【i】=max(dp【m】【i+1】-mp[m][i],1)。
到达最后一列时只能往下边走所以dp【i】【n】=max(dp【i+1】【n】-mp【i】【n】,1)。
其余情况有两种选择所以dp【i】【j】=max(min(dp【i+1】【j】,dp【i】【j+1】)-mp【i】【j】,1)。
(文字表达能力有限,在下尽力了,如有错误还请指正)
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxn=510;
int dp[maxn][maxn],mp[maxn][maxn];
int main()
{
int t,i,j,m,n;
cin>>t;
while(t--)
{
cin>>m>>n;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
cin>>mp[i][j];
dp[m][n]=1;
for(i=m-1;i>=1;i--)
dp[i][n]=max(1,dp[i+1][n]-mp[i][n]);
for(i=n-1;i>=1;i--)
dp[m][i]=max(dp[m][i+1]-mp[m][i],1);
for(i=m-1;i>=1;i--)
for(j=n-1;j>=1;j--)
dp[i][j]=max(min(dp[i+1][j],dp[i][j+1])-mp[i][j],1);
cout<<dp[1][1]<<endl;
}
return 0;
}