分开走(错误)
见如下代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 50;
int a[N][N];
int f[N][N];
int pre[N][N];//1表示从上面来 0表示从左边来
int main(){
int n;
cin >> n;
int i,j,c;
while(cin>>i>>j>>c,i||j||c){
a[i][j]=c;
}
memset(f,-1,sizeof(f));
memset(pre,-1,sizeof(pre));
f[1][0]=f[0][1]=0;
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
if(f[i-1][j]>f[i][j-1]){
f[i][j]=f[i-1][j]+a[i][j];
pre[i][j]=1;
}else{
f[i][j]=f[i][j-1]+a[i][j];
pre[i][j]=0;
}
}
}
pre[1][1]=-1;
int ans = f[n][n];
int nowi=n,nowj=n;
while(1){
a[nowi][nowj]=0;
//如果从上面来
if(pre[nowi][nowj]==1){
--nowi;
}
else if(pre[nowi][nowj]==0){
--nowj;
}else break;
}
memset(f,0,sizeof(f));
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
f[i][j]=max(f[i-1][j],f[i][j-1])+a[i][j];
}
}
ans+=f[n][n];
cout << ans << '\n';
return 0;
}
当输入
7
1 3 2
1 4 3
2 3 3
3 3 3
5 5 4
6 5 4
7 3 2
7 5 4
0 0 0
会输出23
而正确答案是25
因此我们需要同时走;
正解
考虑用 f ( i 1 , j 1 , i 2 , j 2 ) f(i_1,j_1,i_2,j_2) f(i1,j1,i2,j2)
来 表 示 点 ( i 1 , j 1 ) 和 点 ( i 2 , j 2 ) 来表示点 (i_1,j_1) 和点(i_2,j_2) 来表示点(i1,j1)和点(i2,j2)
因为同一个点只能取一次,第二次就变成0了;
只有当 i 1 + j 1 = = i 2 + j 2 i_1+j_1 == i_2+j_2 i1+j1==i2+j2的时候
才有可能出现 i 1 = = i 2 且 j 1 = = j 2 i_1==i_2且j_1==j_2 i1==i2且j1==j2
因此我们令 k = i 1 + j 1 = i 2 + j 2 k = i_1+j_1 = i_2+j_2 k=i1+j1=i2+j2
现在我们可以将四维DP优化到三维DP了
即 f ( k , i 1 , i 2 ) 来 替 代 f ( i 1 , j 1 , i 2 , j 2 ) f(k,i_1,i_2) 来替代f(i_1,j_1,i_2,j_2) f(k,i1,i2)来替代f(i1,j1,i2,j2)
Code
#include <iostream>
#include <cstring>
using namespace std;
const int N = 50;
int a[N][N];
int f[N<<1][N][N];
int main(){
int n;
cin >> n;
int i,j,c;
while(cin>>i>>j>>c,i||j||c){
a[i][j]=c;
}
for(int k=2;k<=n<<1;++k){
for(int i1=1;i1<=n;++i1){
for(int i2=1;i2<=n;++i2){
int j1 = k - i1,j2 = k - i2,sum;
if(j1<1||j1>n||j2<1||j2>n) continue;
//只能加一次
if(i1==i2){
sum = a[i1][j1];
}
else{
sum = a[i1][j1]+a[i2][j2];
}
//下下、下右、右下、右右四种情况
int &t = f[k][i1][i2];
t = max(t,f[k-1][i1-1][i2-1]+sum);
t = max(t,f[k-1][i1][i2-1]+sum);
t = max(t,f[k-1][i1-1][i2]+sum);
t = max(t,f[k-1][i1][i2]+sum);
}
}
}
cout << f[n<<1][n][n] << '\n';
return 0;
}