P1004 方格取数https://www.luogu.org/problemnew/show/P1004
P1006 传纸条https://www.luogu.org/problemnew/show/P1006
方格取数:
设f(i,j,k,l)为从原点分别走两条路径分别到(i,j),(k,l)所取得的和。
状态转移方程:f[i][j][k][l]=max{f[i−1][j][k−1][l],f[i−1][j][k][l−1],f[i][j−1][k−1][l],f[i][j−1][k][l−1]}+a[i][j]+a[k][l],若(i,j)(k,l)不是同点
若(i,j)(k,l)为同点,f[i][j][k][l]=max{f[i−1][j][k−1][l],f[i−1][j][k][l−1],f[i][j−1][k−1][l],f[i][j−1][k][l−1]}+a[i][j]
递推边界:f(1,1,1,1)=a[1][1].
边界之外:f(_,_,_,_)中_含0则状态值为0.
正确性:若i+j==k+l,则该状态值是正确的,否则不正确,但正确答案不受影响,因为答案所转移的状态都是正确的。
//方格取数递推
#include<bits/stdc++.h>
using namespace std;
int n,a[10][10],d[10][10][10][10];
int Max(int a,int b,int c,int d)
{
int x=max(a,b),y=max(c,d);
return max(x,y);
}
int main()
{
freopen("input.in","r",stdin);
int x,y,z;
cin>>n;
while((cin>>x>>y>>z)&&x)a[x][y]=z;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=1;k<=n;k++)
{
for(int l=1;l<=n;l++)
{
d[i][j][k][l]=Max(d[i-1][j][k-1][l],d[i-1][j][k][l-1],d[i][j-1][k-1][l],d[i][j-1][k][l-1]);
d[i][j][k][l]+=((i==k&&j==l)?(a[i][j]):(a[i][j]+a[k][l]));
}
}
}
}
cout<<d[n][n][n][n]<<endl;
return 0;
}
//方格取数-记忆化搜索
#include<bits/stdc++.h>
using namespace std;
int n,a[10][10],d[10][10][10][10];
int Max(int a,int b,int c,int d)
{
int x=max(a,b),y=max(c,d);
return max(x,y);
}
int f(int i,int j,int k,int l)
{
if(i==0||j==0||k==0||l==0)return 0;
if(d[i][j][k][l]!=-1)return d[i][j][k][l];
if(i==1&&j==1&&k==1&&l==1)return d[i][j][k][l]=a[1][1];
d[i][j][k][l]=Max(f(i-1,j,k-1,l),f(i-1,j,k,l-1),f(i,j-1,k-1,l),f(i,j-1,k,l-1));
d[i][j][k][l]+=((i==k&&j==l)?(a[i][j]):(a[i][j]+a[k][l]));
return d[i][j][k][l];
}
int main()
{
freopen("input.in","r",stdin);
int x,y,z;
cin>>n;
while((cin>>x>>y>>z)&&x)a[x][y]=z;
memset(d,-1,sizeof(d));
cout<<f(n,n,n,n)<<endl;
return 0;
}
传纸条:
设f(i,j,k,l)为从原点到(i,j)(k,l)分别走一条不相交的道路的好心程度之和。
状态转移方程:f[i][j][k][l]=max{f[i−1][j][k−1][l],f[i−1][j][k][l−1],f[i][j−1][k−1][l],f[i][j−1][k][l−1]}+a[i][j]+a[k][l],若(i,j)(k,l)不是同点
若(i,j)(k,l)为同点,f[i][j][k][l]=0.
递推边界:f(1,1,1,1)=a[1][1].
边界之外:f(_,_,_,_)中_含0则状态值为0.
正确性:若i+j==k+l,则该状态值是正确的,否则不正确,但正确答案不受影响,因为答案所转移的状态都是正确的。
//传纸条递推 四维
#include<bits/stdc++.h>
using namespace std;
int n,m,a[55][55],d[55][55][55][55];
int Max(int a,int b,int c,int d)
{
int x=max(a,b),y=max(c,d);
return max(x,y);
}
void debug()
{
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
for(int l=1;l<=n;l++){
printf("(%d,%d)&(%d,%d):%d ",i,j,k,l,d[i][j][k][l]);
if(l==n)putchar('\n');
}
}
}
}
}
int main()
{
freopen("input.in","r",stdin);
cin>>n>>m;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>a[i][j];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
for(int k=1;k<=n;k++)
{
/*
for(int l=j+1;l<=m;l++)
{
d[i][j][k][l]=a[i][j]+a[k][l]+Max(d[i-1][j][k-1][l],d[i-1][j][k][l-1],d[i][j-1][k-1][l],d[i][j-1][k][l-1]);
}
*/
for(int l=1;l<=m;l++)
{
if(i==k&&j==l)d[i][j][k][l]=0;
else d[i][j][k][l]=a[i][j]+a[k][l]+Max(d[i-1][j][k-1][l],d[i-1][j][k][l-1],d[i][j-1][k-1][l],d[i][j-1][k][l-1]);
}
}
}
}
//debug();
cout<<d[n][m-1][n-1][m]<<endl;
return 0;
}
//传纸条-三维dp
#include<bits/stdc++.h>
using namespace std;
int m,n,a[55][55],d[105][55][55];
int Max(int a,int b,int c,int d)
{
int x=max(a,b),y=max(c,d);
return max(x,y);
}
int main()
{
// freopen("input.in","r",stdin);
cin>>n>>m;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>a[i][j];
d[3][1][2]=a[1][2]+a[2][1];
for(int k=4;k<n+m;k++)
{
for(int i=1;i<m;i++)
{
for(int j=i+1;j<=m;j++)
{
if(i<k-n||j>k-1)continue;
d[k][i][j]=Max(d[k-1][i-1][j-1],d[k-1][i][j],d[k-1][i-1][j],d[k-1][i][j-1]);
d[k][i][j]+=a[k-i][i]+a[k-j][j];
}
}
}
cout<<d[n+m-1][m-1][m]<<endl;
return 0;
}
程序有很多地方需要注意。开始时自己对四维DP的正确性非常怀疑,只好手+脑分析小数据,说是小,3*3的矩阵有3^4=81中状态,费了两天,终于大概想通了一些。