题意分析
一开始想的是进行两遍DP,这样明显是错误的,因为会有重复走的位置,这是不符合题意的。
然后看题解,很多写的是双线程DP,我不太感觉的出来。其实想一下,因为有两个人要描述这两个人的状态,那肯定是分别用他们在平面的坐标,这是最直接的。
dp[x1][y1][x2][y2]dp[x1][y1][x2][y2] 表示A走到(x1,y1)(x1,y1), B走到(x2,y2)(x2,y2) 时最大的值。
有了状态定义,然后就可以考虑状态的转移了。
题目要求是传过来传回去,其实这样是和同时从起点到终点,选择两条不同的路径是等价的。
只能向右和向下走,所以 当前状态=max(所有转移到AB所在点的可能)+A所在点的值+B所在点的值当前状态=max(所有转移到AB所在点的可能)+A所在点的值+B所在点的值。
如果这么写肯定错了,因为还要注意AB的坐标不能相同,也就是AB不应该从除终点外的一个点开始转移,不能转移到除起点外的同一个点。
注意了这点,大胆的写记忆花搜索,或者DP都是可以的。
代码总览
#include<bits/stdc++.h>
using namespace std;
const int nmax = 60;
int mp[nmax][nmax];
int dp[nmax][nmax][nmax][nmax];
int n,m;
int nx[2] = {0,1};
int ny[2] = {1,0};
bool leagal(int x1, int y1, int x2, int y2){
if(x1 < 1 || x1 > n || y1 < 1 || y1 > m) return false;
if(x2 < 1 || x2 > n || y2 < 1 || y2 > m) return false;
if(x1 == x2 && y2 == y1 && (x1 != n || y1 != m)) return false;
return true;
}
int getdp(int x1, int y1, int x2, int y2){
if(dp[x1][y1][x2][y2]) return dp[x1][y1][x2][y2];
for(int i = 0;i<2;++i){
int xx1 = x1 + nx[i];
int yy1 = y1 + ny[i];
for(int j = 0;j<2;++j){
int xx2 = x2 + nx[j];
int yy2 = y2 + ny[j];
if(leagal(xx1,yy1,xx2,yy2)){
dp[x1][y1][x2][y2] = max(dp[x1][y1][x2][y2], getdp(xx1,yy1,xx2,yy2) + mp[x1][y1] + mp[x2][y2]);
}
}
}
return dp[x1][y1][x2][y2];
}
int main(){
scanf("%d %d",&n,&m);
for(int i = 1;i<=n;++i){
for(int j = 1;j<=m;++j){
scanf("%d",&mp[i][j]);
}
}
printf("%d\n",getdp(1,1,1,1));
return 0;
}