【NOIP2008提高组T3】传纸条-双线程动态规划

本文介绍了一个使用双线程动态规划方法解决在一个m*n矩阵中找到两条不相交路径,使得路径上点的权值之和最大的问题。通过枚举步数k,实现了对所有可能路径的遍历并计算出最大权值。

(本人本题完成于2016-7-18)

题目大意:一个m*n的矩阵,要从(1,1)开始寻找两条不同路径到(m,n),使经过的点权之和最大,输出这个最大值。

分析:双线程dp,f[i][j]表示当一条路线(以下称为A路线)走到第i行,另一条路线(以下称为B路线)走到第j行时,已得到的点权之和最大值,不难得出f[i][j]可由四种情况推出:f[i][j](前一步A路线走到i行,B路线走到j行),f[i-1][j](前一步A路线走到i-1行,B路线走到j行),f[i][j-1](前一步A路线走到i行,B路线走到j-1行),f[i-1][j-1](前一步A路线走到i-1行,B路线走到j-1行),设当前所走步数为k,可以得出A路线走到(i,k+2-i),B路线走到(j,k+2-j),则状态转移方程为:

f[i][j]=max(f[i][j],f[i-1][j],f[i][j-1],f[i-1][j-1])+(i,k+2-i)点权+(j,k+2-j)点权


以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
long m,n,f[51][51]={0},v[51][51]={0}; //m为行数,n为列数,v[i][j]为(i,j)的点权

int main()
{
  scanf("%ld %ld",&m,&n);
  for(int i=1;i<=m;i++)
    for(int j=1;j<=n;j++)
	  scanf("%ld",&v[i][j]);
  
  for(int k=0;k<=n+m-2;k++) //k枚举步数
    for(int i=m;i>=1;i--)
	  for(int j=m;j>=1;j--) //注意i,j两重循环顺序,保证结果正确性
	  {
	    if (k==n+m-2&&i==m&&j==m) //最后的结果特殊判断
		  f[i][j]=max(max(f[i][j],f[i-1][j-1]),max(f[i-1][j],f[i][j-1]))+v[i][k+2-i]+v[j][k+2-j];
	    else if (i!=j&&k+2-i>=1&&k+2-j>=1) //条件i!=j是为防止两路线走到同一个点,其余两个条件是为防止坐标超越边界
		  f[i][j]=max(max(f[i][j],f[i-1][j-1]),max(f[i-1][j],f[i][j-1]))+v[i][k+2-i]+v[j][k+2-j];
	  }
  
  printf("%ld",f[m][m]);
  return 0;
}

### NOIP2008 提高 纸条 题解算法实现 #### 动态规划状态定义 对于这个问题,采用四维动态规划来解决。设 `f[i][j][p][q]` 表示第一张纸条递到位置 `(i, j)` 和第二张纸条递到位置 `(p, q)` 所累积的好心程度总和[^1]。 #### 边界条件初始化 初始状态下,当两个起点都位于左上角即 `(0, 0)` 时,好心程度为起始值。因此设置边界条件如下: ```cpp // 假定地图大小为m*n int f[m][n][m][n]; memset(f, -0x3f, sizeof(f)); // 初始化所有路径权值为极小值 f[0][0][0][0] = grid[0][0]; // 起点处的好心程度等于该点的数值 ``` #### 状态转移方程 为了计算任意时刻的状态,需要考虑四个方向上的移动可能性,并从中选取最优解作为当前状态的最佳选择。具体来说就是从上方、左侧以及斜向相邻的位置继承而来并加上当前位置的价值。注意这里要排除两条路线重合的情况以防止重复计数: ```cpp for(int i=0;i<m;++i){ for(int j=0;j<n;++j){ for(int p=i;p>=0&&p<m;++p){ int lowbound=(i==p)?(j+1):0; for(int q=lowbound;q<n;++q){ if(x1_new < m && y1_new < n && x2_new < m && y2_new < n)[^2]: // 更新dp表中的最大值 f[i][j][p][q]=max({ f[x1-1][y1][x2-1][y2], // 上->上 f[x1-1][y1][x2][y2-1], // 上->右 f[x1][y1-1][x2-1][y2], // 左->上 f[x1][y1-1][x2][y2-1] // 左->右 }) + ((i!=p || j!=q)?grid[p][q]:0)+grid[i][j]; } } } } ``` #### 结果获取 最终的结果保存在终点坐标对应的数元素内,即 `f[m-1][n-1][m-1][n-1]` 中存储的就是整个过程中可以获得的最大好心程度之和。 ```cpp cout << f[m-1][n-1][m-1][n-1]<<endl; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值