给定一个 N*M 的矩阵A,每个格子中有一个整数。现在需要找到两条从左上角 (1,1) 到右下角 (N,M) 的路径,路径上的每一步只能向右或向下走。路径经过的格子中的数会被取走。两条路径不能经过同一个格子。求取得的数之和最大是多少。N,M≤50。
第一行有2个用空格隔开的整数n和m,表示有n行m列(1<=n,m<=50)。
接下来的n行是一个n*m的矩阵,每行的m个整数之间用空格隔开。
一个整数,表示答案。
3 3
0 3 9
2 8 5
5 7 0
34
30%的数据满足:1<=m,n<=10
100%的数据满足:1<=m,n<=50
我们不妨假设f[i,x1,x2]表示路径当前长度为i,第一条路径末尾在第x1行,坐标为(x1,y1),第二条路径末尾在x2行,坐标为(x2,y2)。
原数组为A;
显然有x1+y1=x2+y2=i+2
所以 y1=i+2-x1,y2=i+2-x2。
每条路径都有向下和向右两种扩展方法。
故共有2 * 2 = 4 种转移。以下是具体转移
1 都向右走
两条路径同时经过一个点,只算一次下面同理
如果x1=x2 且 y1+1=y2+1
f[i+1,x1,x2]=max(f[i+1,x1,x2],f[i,x1,x2]+A[x1,y1+1]);
否则
f[i+1,x1,x2]=max(f[i+1,x1,x2],f[i,x1,x2]+A[x1,y1+1]+A[x2,y2+1])
2 都向下走
如果x1+1=x2+1且 y1=y2
f[i+1,x1+1,x2+1]=max(f[i+1,x1+1,x2+1],f[i,x1,x2]+A[x1+1,y1]);
否则
f[i+1,x1+1,x2+1]=max(f[i+1,x1+1,x2+1],f[i,x1,x2]+A[x1+1,y1]+A[x2+1,y2])
3 第一条向右,第二条向下
如果x1=x2+1 且 y1+1=y2
f[i+1,x1,x2+1]=max(f[i+1,x1,x2+1],f[i,x1,x2]+A[x1,y1+1])
否则
f[i+1,x1,x2+1]=max(f[i+1,x1,x2+1],f[i,x1,x2]+A[x1,y1+1]+A[x2+1,y2])
4 第一条向下,第二条向右
如果x1+1=x2 且 y1=y2+1
f[i+1,x1+1,x2]=max(f[i+1,x1+1,x2],f[i,x1,x2]+A[x1+1,y1])
否则
f[i+1,x1+1,x2]=max(f[i+1,x1+1,x2],f[i,x1,x2]+A[x1+1,y1]+A[x2,y2+1])
#include
#include
#include
using namespace std;
int f[102][51][51];
int A[51][51];
int main(){
int n,m,i,j,x1,x2,y1,y2;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&A[i][j]);
f[0][1][1]=A[1][1];
for(i=0;i<=n+m-1;i++){
for(x1=1;x1<=n;x1++){
for(x2=1;x2<=n;x2++){
y1=i+2-x1;
y2=i+2-x2;
if(y1<1||y2<1)continue;
if(x1==x2&&y1+1==y2+1)
f[i+1][x1][x2]=max(f[i+1][x1][x2],f[i][x1][x2]+A[x1][y1+1]);
else
f[i+1][x1][x2]=max(f[i+1][x1][x2],f[i][x1][x2]+A[x1][y1+1]+A[x2][y2+1]);
if(x1+1==x2+1&&y1==y2)
f[i+1][x1+1][x2+1]=max(f[i+1][x1+1][x2+1],f[i][x1][x2]+A[x1+1][y1]);
else
f[i+1][x1+1][x2+1]=max(f[i+1][x1+1][x2+1],f[i][x1][x2]+A[x1+1][y1]+A[x2+1][y2]);
if(x1==x2+1&&y1+1==y2)
f[i+1][x1][x2+1]=max(f[i+1][x1][x2+1],f[i][x1][x2]+A[x1][y1+1]);
else
f[i+1][x1][x2+1]=max(f[i+1][x1][x2+1],f[i][x1][x2]+A[x1][y1+1]+A[x2+1][y2]);
if(x1+1==x2&&y1==y2+1)
f[i+1][x1+1][x2]=max(f[i+1][x1+1][x2],f[i][x1][x2]+A[x1+1][y1]);
else
f[i+1][x1+1][x2]=max(f[i+1][x1+1][x2],f[i][x1][x2]+A[x1+1][y1]+A[x2][y2+1]);
}
}
}
printf("%d",f[n+m-2][n][n]);
return 0;
}
上面的做法可能有些复杂,下面我讲一下一个比较暴力的做法
假设dp[x1,x2,x3,x4]表示第一条路径到达(x1,x2)第二条路径到达(x3,x4)所得到的最大值;
原数组为A;
显然有dp[x1,x2,x3,x4]=max(max(dp[x1-1,x2,x3-1,x4],dp[x1-1,x2,x3,x4-1]),max(dp[x1,x2-1,x3-1,x4],dp[x1,x2-1,x3,x4-1]))+A[x1,x2]+A[x3,x4];
如果两条路径走到了同一个位置,则需要特判。
#include
#include
using namespace std;
int dp[52][52][52][52];
int A[52][52];
int main(){
int n,m;
scanf("%d%d",&n,&m);
int i,j,x1,x2,x3,x4;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&A[i][j]);
for(x1=1;x1<=n;x1++){
for(x2=1;x2<=m;x2++){
for(x3=1;x3<=n;x3++){
for(x4=1;x4<=m;x4++){
dp[x1][x2][x3][x4]=max(max(dp[x1-1][x2][x3-1][x4],dp[x1-1][x2][x3][x4-1]),max(dp[x1][x2-1][x3-1][x4],dp[x1][x2-1][x3][x4-1]))+A[x1][x2]+A[x3][x4];
if(x1==x3&&x2==x4)
dp[x1][x2][x3][x4]-=A[x1][x2];
}
}
}
}
printf("%d",dp[n][m][n][m]);
return 0;
}