[NOIP 2008]传纸条

感觉这道题出得不错,而且网上题解讲得非常好,所以~~~~~~~~~~

使用动态规划算法的关键,在于状态的定义和找到动态转移方程。
很多同学(包括我)在内最初考虑该算法的时候一定是在想:使用DP[i][j]来保存[0][0]到[i][j]的来回最大值。不过很明显,这种状态的保存只保存得了一条路径,而如果DP两次,图的状态怎么确定又是很大的问题。所以,不能这样保存状态。

既然要走两次,而题目规定了往下走只有右、下两个方向,往上走只有左、上两个方向,所以走的步数来回肯定一样的。

因此,我们不妨让两条线路同时进行,即用四维数组DP[i][j][m][n]来保存状态。
而又因为同一条路线可以是往下走的,也可以是往回走的(这只是一个相对的问题),所以可以直接假设两条线路都是从上往下走(怎么走无所谓,该线路的值是一定的)。

接下来解释一下数组的含义:DP[i][j][m][n]中事实上相当于保存了DP[i][j]和DP[m][n]之和,它的意思是第一条路线走到(i,j)处的时候第二条路线走到(m,n)处时所经过地点的好心值之和的最大值。

为了确保两条线路不会重复,我们人为规定 m > i+1 ,依旧使用四重for循环来进行状态转移。

我们的四重循环是这样的:

 //用x、y来分别作为行、列
 for(int i=0;i<x;i++)
  for(int j=0;j<y;j++)
   for(int m=i+1;m<x;m++)
    for(int n=j-1;n>=0;n--)
 

开四重循环的话,这次题目要求比较低,只有50*50,如果大点呢?四重循环100^4不就超时了吗?。
所以,我们就考虑一下将状态压缩。

我们已经注意到,两条路线在同一时刻走的路程是相同的。

因此不难发现:
i+j=m+n。
n=i+j-m。
如此一来我们便可以省去 n 这一维数组,只剩下压缩之后的三位数组。
这个的正确性也不难证明,m+n中,m一定,n也一定是确定下来了的。

新的方程就不难列出来了:

for(int i=0;i<x;i++)
 for(int j=0;j<y;j++)
  for(int m=i+1;m<i+j;m++)//想想为什么是i+j
  {
    状态转移方程
  }

注意:状态的转移中会出现一些边界情况,如 i=0 和 j=0,需要特殊判断。

核心代码:

memset(dp,0,sizeof(dp));
for(int i=0;i<m;i++)//不断向下走
 for(int j=0;j<n;j++)//向右走
  for(int k=i+1;k<=i+j;k++)//向右或者向下走,所以k>i+1
  {
	   if(k==i+1)//两条线在靠近的两行,以下部分可画图验证。
	   {
		   if(i==0)//处理当在首行时的边界条件
		   {
				dp[i][j][k]=dp[i][j-1][k]+map[i][j]+map[k][i+j-k];//只可能有一种情况
		   }
		   else if(j==0)//处理在首列时候的边界条件
		   {
				continue;//不存在这种情况
		   }
		   else
		   {
				dp[i][j][k]=max(max(dp[i-1][j][k],dp[i-1][j][k-1]),dp[i][j-1][k])+map[i][j]+map[k][i+j-k];//三种情况都考虑
		   }
	   }
       else
	   {
			if(i==0)//同上的都不解释了
			{
				dp[i][j][k]=max(dp[i][j-1][k-1],dp[i][j-1][k])+map[i][j]+map[k][i+j-k];
			}
			else if(j==0)
			{
				dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j][k-1])+map[i][j]+map[k][i+j-k];
			}
			else
			{
				dp[i][j][k]=max(max(dp[i-1][j][k],dp[i-1][j][k-1]),max(dp[i][j-1][k-1],dp[i][j-1][k]))+map[i][j]+map[k][i+j-k];
			}
       }
   }
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值