洛谷P1004 方格取數 & P1006 傳紙條
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中狀態,費了兩天,終於大概想通了一些。