题1:NYOJ 61(传纸条),先看一个人的情况,DP[i][j]表示纸条到达(i,j)的时候好心程度之和的最大值,那么由于只能向下或者向右走,那么到达(i,j)位置可能是由(i-1,j)下移一步或者由(i,j-1)位置,右移一步得到,所以动态转移方程为:DP[i][j]=max(DP[i-1][j],DP[i][j-1])+G[i][j]。(G[i][j]表示(i,j)这个位置的人的好心程度)。
题目的意思是先从位置(0,0)传到(n,m),再由(n,m)传到(0,0),那么我可以假设从(0,0)这个位置由两条路径同时向(n,m)传,只要这两条路径中不含相同的点,则结果也是一样的,用DP[i][j][p][q]表示一条路径由(0,0)传到(i,j)和另一条路径由(0,0)传到(p,q)之间的好心程度只和的最大值,最终结果都是到达(n,m),所以只需输出DP[n][m][n][m]即可。
动态规划方程为:DP[i][j][p][q]=max(DP[i-1][j][p-1][q],DP[i][j-1][p-1][q],DP[i-1][j][p][q-1],DP[i][j-1][p][q-1])+G[i][j]+G[p][q]。
由于每次都只能是向下或者向右移动,那么位置变化也只能是纵坐标+1或者横坐标+1,又是从(0,0)开始,所以若到达(i,j)位置,那么到达(i,j)位置经过的步数为i+j,所以经过相同的步数后必定满足:i+j=p+q。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int MAX=52;
#define max(a,b) (a)>(b)?(a):(b)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
int n,m,G[MAX][MAX],DP[MAX][MAX][MAX][MAX];
int Max(int a,int b,int c,int d)
{ return max(max(a,b),max(c,d));
}
int main()
{ int Case;
CLR(DP,0);
scanf("%d",&Case);
while(Case--)
{ scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&G[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int p=i+1,q;p<=n;p++)
{ int q=i+j-p;
if(q<=0) continue; //这条语句一定要加
DP[i][j][p][q]=Max(DP[i-1][j][p-1][q],DP[i-1][j][p][q-1],
DP[i][j-1][p-1][q],DP[i][j-1][p][q-1])
+G[i][j]+G[p][q];
}
int res=Max(DP[n-1][m][n-1][m],DP[n-1][m][n][m-1],
DP[n][m-1][n-1][m],DP[n][m-1][n][m-1]);
printf("%d\n",res);
}
return 0;
}