AcWing275 洛谷P1006 NOIP 2008提高组第三题 传纸条

AcWing275 洛谷P1006 NOIP 2008提高组第三题 传纸条

描述(已对原题进行了抽象)

给定一个 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

题解1:

我们不妨假设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 <iostream>
#include <cstdio>
#include <cstring> 
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;
}

题解2:

上面的做法可能有些复杂,下面我讲一下一个比较暴力的做法
假设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 <iostream>
#include <cstdio>
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;
}

猜你喜欢

转载自blog.csdn.net/linwenqidbk/article/details/90742938
今日推荐