洛谷P1006 [NOIP2008 提高组] 传纸条

洛谷P1006 [NOIP2008 提高组] 传纸条

题目传送门

注意了:题面有坑!

先输入m,后输入n!

看到n的范围……50!O(n^4)可以过!
我们用四层循环来分别枚举来回所走的两个点。
详细讲解见代码。

#include<bits/stdc++.h>
using namespace std;
int dp[55][55][55][55],a[55][55],n,m;
int main()
{
	scanf("%d%d",&m,&n);
	for(int i=1;i<=m;i++)
		for(int j=1;j<=n;j++)
			scanf("%d",&a[i][j]);
	for(int i=1;i<=m;i++)
		for(int j=1;j<=n;j++)
			for(int k=1;k<=m;k++)
				for(int t=1;t<=n;t++)
				{
					int t1=max(dp[i-1][j][k][t-1],dp[i][j-1][k-1][t]);
					int t2=max(dp[i-1][j][k-1][t],dp[i][j-1][k][t-1]);
					dp[i][j][k][t]=max(t1,t2)+a[i][j]+a[k][t];
					if(i==k&&j==t)//同一位置只加一次
						dp[i][j][k][t]-=a[i][j];
				}
	printf("%d\n",dp[m][n][m][n]);
	return 0;
}

当然这肯定不是正解也能AC
正解的复杂度是O((n+m)*n^2)≈ O(n^3)
解法如下:
我们可以发现,从左上角到右下角所走不数的最大值是n+m-1。
证明:
在这里插入图片描述
我们要从A到B,假设黑色是路径,那么路径我们可以转化成:
在这里插入图片描述
方法是平移线段,就像小学求楼梯周长一样。
那么我们就可以按步数转移。
代码如下:

#include<bits/stdc++.h>
using namespace std;
const int oo=1e9;
const int M=105;
int g[55][55],dp[M][55][55];
int Max(int a,int b,int c,int d) {return max(a,max(b,max(c,d)));}//正常max函数只能比较两个值只能比较两个值,所以我们要自己写一个max函数
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&g[i][j]);
	dp[1][1][1]=g[1][1];
	for(int i=2;i<=n+m-1;i++)
		for(int x1=1;x1<=n;x1++)//第一个点枚举横坐标
			{
				if(i+1-x1>=1&&i+1-x1<=m)
				{
					for(int x2=1;x2<=n;x2++)//枚举第二个点横坐标
					{
						if(i+1-x2>=1&&i+1-x2<=m)
							dp[i][x1][x2]=Max(dp[i-1][x1][x2],dp[i-1][x1-1][x2],dp[i-1][x1][x2-1],dp[i-1][x1-1][x2-1]);
							if(x1==x2)//如果同一位置就加一次
								dp[i][x1][x2]+=g[x1][i+1-x1];
							else
								dp[i][x1][x2]+=g[x1][i+1-x1]+g[x2][i+1-x2];
					}
				}
			}
	printf("%d",dp[n+m-1][n][n]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_50624971/article/details/115029168