http://icpc.upc.edu.cn/problem.php?cid=1675&pid=0
这一道题的思路很简单,暴力DP都可以过(就是用4个for),但是有可能会爆空间。
先来讲讲暴力DP的思路吧
这一道题可以看成是求一个人从左上角到右下角走两次所经过路线的最大值,所以用两个for来枚举第一次走的横纵坐标,另外两个for来枚举第二次做的横纵坐标,
一共分四种情况:
1、f[i][j][k][l]=f[i-1][j][k-1][l]//两条路都是从上面走下来的
2、f[i][j][k][l]=f[i][j-1][k][l-1]//两条都是从左边走过来的
3、f[i][j][k][l]=f[i-1][j][k][l-1]//一条是从上面走下来的,一条是从左边走过来的
4、f[i][j][k][l]=f[i][j-1][k-1][l]//一条是从左边走过来的,一条是从上面走下来的
可以推出状态转移方程:f[i][j][k][l]=max(max(f[i-1][j][k-1][l],f[i][j-1][k][l-1]),max(f[i-1][j][k][l-1],f[i][j-1][k-1][l]))+a[i][j];
还要加上一条判断语句:if(i!=k&&j!=l) f[i][j][k][l]+=a[k][l];
完整代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
int m,n;
int a[55][55];
ll dp[55][55][55][55];
int main()
{
scanf("%d%d",&m,&n);
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&a[i][j]);
}
}
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=m;k>=1;k--)
{
for(int l=n;l>=1;l--)
{
ll x=max(dp[i-1][j][k-1][l],dp[i][j-1][k][l-1]);
ll y=max(dp[i-1][j][k][l-1],dp[i][j-1][k-1][l]);
dp[i][j][k][l]=max(x,y)+a[i][j];
if(i!=k && j!=l)
dp[i][j][k][l]+=a[k][l];
}
}
}
}
printf("%lld\n",dp[m][n][m][n]);
return 0;
}
这种方法既浪费空间,又容易超时,所以我们对它进行了时间的优化
因为两个人到目的地的步数相同,所以枚举步数和两个人的横坐标之后,就可以算出两个人纵坐标,
但是状态转移方程就要复杂一点:(因为枚举步数,就要加上每个人的位置的值)
f[k][i][j]=max(max(f[k-1][i][j],f[k-1][i-1][j-1]),max(f[k-1][i-1][j],f[k-1][i][j-1]))+a[i][k+1-i]+a[j][k+1-j];
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
int m,n;
int a[55][55];
ll dp[105][55][55];
int main()
{
scanf("%d%d",&m,&n);
for(int i=1; i<=m; i++)
{
for(int j=1; j<=n; j++)
{
scanf("%d",&a[i][j]);
}
}
for(int k=1; k<=m+n-1; k++)
{
int m1=min(m,k);
for(int i=1; i<=m1; i++)
{
for(int j=1; j<=m1; j++)
{
ll c=a[i][k-i+1]+a[j][k-j+1];
ll x=max(dp[k-1][i][j],dp[k-1][i-1][j-1]);
ll y=max(dp[k-1][i-1][j],dp[k-1][i][j-1]);
ll t=max(x,y)+c;
dp[k][i][j]=t;
if(i==j)
dp[k][i][j]-=c/2;
}
}
}
printf("%lld\n",dp[m+n-1][m][m]);
return 0;
}
这样,既节省了空间,又能给程序提速。最终空间为100*50*50。
还有一种最优的办法:枚举一个人的横纵坐标和另一个人的横坐标,因为步数相同,就可以算出另一个人的纵坐标
用同样的办法,可以找出状态转移方程。
f[i][j][k]=max(max(f[i-1][j][k-1],f[i-1][j][k]),max(f[i][j-1][k-1],f[i][j-1][k]))+a[i][j]+a[k][l];//l=i+j-k;
最终空间为50*50*50。
#include<iostream>
#include<cstdio>
using namespace std;
int a[60][60],f[60][60][60],n,m,i,j,k,l;
int main()
{
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
for(k=1;k<=i+j-1;k++){
l=i+j-k;
if(i==k&&j==l&& !(i==n&&j==m))
continue;
f[i][j][k]=max(max(f[i-1][j][k-1],
f[i-1][j][k]),
max(f[i][j-1][k-1],
f[i][j-1][k]))+a[i][j]+a[k][l];
}
printf("%d",f[n][m][n]);
}