习题:蒜头君的城堡之旅

蒜国地域是一个 n 行 m 列的矩阵,下标均从 1 开始。蒜国有个美丽的城堡,在坐标 (n,m) 上,蒜头君在坐标 (1,1) 的位置上。蒜头君打算出发去城堡游玩,游玩结束后返回到起点。在出发去城堡的路上,蒜头君只会选择往下或者往右走,而在返回的路上,蒜头君只会选择往上或者往左走,每次只能走一格。已知每个格子上都有一定数量的蒜味可乐,每个格子至多经过一次。现在蒜头君请你来帮他计算一下,如何计划来回行程,可以收集到最多的蒜味可乐。

输入格式

第一行输入两个整数 n,m(1≤n,m≤50),表示蒜国是一个 n 行 m 列的矩阵。接下来输入 n 行,每行输入 m 个整数,代表一个 n×m 的矩阵,每个整数代表对应位置上的蒜味可乐数量,每行的每两个整数之间用一个空格隔开。其中蒜头君的位置和城堡的位置上没有蒜味可乐,用 0 表示,其余位置上的整数范围在 [1,100] 内。

输出格式

输出一行,输出一个整数,表示蒜头君在来回路上能收集到的蒜味可乐的最大值。

样例输入

3 3

0 2 9

4 8 6

2 7 0

样例输出

36
 

error:一开始打算用两遍二维dp去了一个就在map上把它置零

#include<iostream>
using namespace std;
int n,m,dp[51][51],map[51][51];
int main()
{
	ios::sync_with_stdio(false);
	int i,j,sx[2],sy[2],index=0;
	cin>>n>>m;
	for(i=1;i<=n;++i)
	{
		for(j=1;j<=m;++j)
		{
			cin>>map[i][j];
			if(map[i][j]==0)
			{
				sx[index]=i;
				sy[index]=j;
				index++;
			}
		}
	}
	for(i=sx[0];i<=sx[1];++i)
	{
         for(j=sy[0];j<=sy[1];++j)
         {
         	if(i==sx[0]&&j!=sy[0])
         	{
            	dp[i][j]=map[i][j]+dp[i][j-1];
            }
            else if(j==sy[0]&&i!=sx[0])
            {
            	dp[i][j]=map[i][j]+dp[i-1][j];
			}
			else
			{
			    dp[i][j]=map[i][j]+max(dp[i-1][j],dp[i][j-1]);
			}
        	map[i][j]=0;
		 }
	}
	cout<<dp[sx[1]][sy[1]]<<endl;
	for(i=1;i<=n;++i)
	{
		for(j=1;j<=m;++j)
		{
			cout<<map[i][j]<<" ";
			
		}
		cout<<endl;
	}
	for(i=sx[1];i>=sx[0];--i)
	{
         for(j=sy[1];j>=sy[0];--j)
         {
         	if(i==sx[1]&&j!=sy[1])
         	{
            	dp[i][j]=map[i][j]+dp[i][j+1];
            }
            else if(j==sy[1]&&i!=sx[1])
            {
            	dp[i][j]=map[i][j]+dp[i+1][j];
			}
			else
			{
			    dp[i][j]=map[i][j]+max(dp[i+1][j],dp[i][j+1]);
			}
        	map[i][j]=0;
		 }
	}
	cout<<dp[sx[0]][sy[0]];
	return 0;
}

这个题目的主要矛盾点在于,去城堡的路上和在城堡回来的路上的权重应当是一样的,即如果取去时路上的最大值和相应的回来的路上的最大值并不能保证其和是最大值。

思路:

1. 看成两个独立的图,每个人都从他的起始点开始走,只是两个图的路线要求不重叠要达到一个坐标点,只有通过这个坐标点的上方或者左方进入,又已知路线不能重复,那么一旦从左方进入这个坐标,那么只能在上方离开这个坐标,反之亦然:从上方进入只能在左方离开。

2. 那么这个问题(去到(n, m)点并返回能取到的最多的可乐的数量)就可以分解为:从(1, 1)出发到(n - 1, m)路上取到的可乐加上从(n, m - 1)出发回到(1, 1)的路上取到的可乐数量之和的最大值(或者相反)。

3. 考虑到虽然出发和返回是两条路线,起始点不一样,但是最终连接的两个点却是一样的。那么就可以转化成这样:有两个人,同时在原点出发,目标都是城堡且两人路线不能重合,最后两个人可以取得的可乐的最大数量的和,就是这个题目的解。用(x1,y1)和(x2,y2)来表示这两个点,那么合法的两个点应当是:x1 != x2 && y1 != y2 (两点不能重复)x1 + y1 == x2 + y2 (两人同时出发,步数相同)

4.可以使用4维数组来表示这个问题的答案:f[x1][y1][x2][y2]:表示在原点出发后,两人分别到达点(x1,y1)和(x2,y2)时所取到的可乐的和的最大值。
--------------------- 
作者:ltknow 
来源:优快云 
原文:https://blog.youkuaiyun.com/ltknow/article/details/80490664 
版权声明:本文为博主原创文章,转载请附上博文链接!


状态转移方程如下(对二人行径方式的举例,列出4种情况)

f[x1][y1][x2][y2] = max(

                                 max(f[x1-1][y1][x2-1][y2],f[x1-1][y1][x2][y2-1]),

                                  max(f[x1][y1-1][x2-1][y2],f[x1][y1-1][x2][y2-1])      )

                                   +map[x1][y1]+map[x2][y2];

 在理解后写出代码

#include<iostream>
#define MAX 53
using namespace std;
int main()
{
	int n,m,x1,x2,y1,y2;
	int map[MAX][MAX]={0},dp[MAX][MAX][MAX][MAX]={0};
	cin>>n>>m;
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
		{
			cin>>map[i][j];
		}
	}
	for(x1=1;x1<=n;++x1)
	{
		for(y1=1;y1<=m;++y1)
		{
			for(x2=1;x2<=n;++x2)
			{
			    y2 = x1+y1-x2;
			    if(y2>m||y2<1) continue;
				if(x1!=x2&&y1!=y2)
				{
    				dp[x1][y1][x2][y2]= max(
			                           max(dp[x1-1][y1][x2-1][y2],dp[x1-1][y1][x2][y2-1]),
									   max(dp[x1][y1-1][x2-1][y2],dp[x1][y1-1][x2][y2-1]))
									   +map[x1][y1]+map[x2][y2];
	            }
			}
		}
	}
	int  a;
    a= max(
                   max(dp[n-1][m][n-1][m] , dp[n-1][m][n][m-1]) ,
                   max(dp[n][m-1][n-1][m] , dp[n-1][m][n-1][m])     );
	cout<<a;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ali]e

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值