一个经典的dp题,典型的错误思想就是做两遍
我们考虑直接做,f[i][j][x][y]表示第一次取道i,j这个位置,第二次到了x,y这个位置
考虑这个i,j和x,y分别是从哪里转移过来,就可以得到方程
f[i][j][x][y]=max(f[i-1][j][x-1][y],f[i][j-1][x-1][y],f[i-1][j][x][y-1],f[i][j-1][x][y-1])+v[x][y]+v[i][j]
这个方程显然有x+y=i+j那么考虑去掉j,y这一维
令f[k][i][j]表示走了k步,分别走到第i行,第j行的答案,转移可以写成
f[k][i][j]=max(f[k-1][i][j],f[k-1][i-1][j-1],f[k-1][i-1][j],f[k-1][i][j-1])+s[i][k-i]+s[j][k-j]
注意如果i=j那么最后那个s[j][k-j]不要加,滚动一下就可以过了
当然这题也有网络流做法,非常的类似:
在左上面建一个源点,与(1,1)点连上,容量为2,费用为0。在右下建一个汇点,与(n,m)连上,容量为2,费用为0.
然后在中间的每个点(i,j),拆成两个点,建立两条边,一条是容量为1,费用为A[i][j]。另一条容量也是1,费用为0。再把左边和上边的点都连上。跑一次费用流就可以了
#pragma GCC opitmize("O3")
#pragma G++ opitmize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n,m,f[210][210],s[210][210];
int main(){
scanf("%d%d",&m,&n);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j) scanf("%d",s[i]+j);
for(int k=2;k<=m+n;++k) for(int i=n;i;--i) for(int j=n;j;--j)
f[i][j]=max(max(f[i][j],f[i-1][j-1]),max(f[i-1][j],f[i][j-1]))+s[i][k-i]+(i!=j?s[j][k-j]:0);
printf("%d\n",f[n][n]);
}