【洛谷P1006】传纸条【DP】

本文探讨洛谷P1006题解,介绍一种求解矩阵中两条不相交路径最大权值之和的动态规划方法,通过优化状态表示和转移方程,将时间复杂度降低至O(n^3+nm)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

题目链接:https://www.luogu.org/problemnew/show/P1006
给出一个 n × m n\times m n×m的矩阵,每个格子中有权值,求从 ( 1 , 1 ) (1,1) (1,1) ( n , m ) (n,m) (n,m)的两条不相交路径使得路径权值之和最大。


思路:

很明显可以把题目看成两条不相交的路径从 ( 1 , 1 ) (1,1) (1,1) ( n , m ) (n,m) (n,m)。那么就可以设 f [ i ] [ j ] [ k ] [ l ] f[i][j][k][l] f[i][j][k][l]表示第一条路走到 ( i , j ) (i,j) (i,j),第二条路走到 ( k , l ) (k,l) (k,l)时的权值最大值。那么由于可以直接从上和下两个方向转移过来,所以就有方程
f [ i ] [ j ] [ k ] [ l ] = m a x ( m a x ( f [ i − 1 ] [ j ] [ k − 1 ] [ l ] , f [ i ] [ j − 1 ] [ k − 1 ] [ l ] ) , m a x ( f [ i − 1 ] [ j ] [ k ] [ l − 1 ] , f [ i ] [ j − 1 ] [ k ] [ l − 1 ] ) ) f[i][j][k][l]=max(max(f[i-1][j][k-1][l],f[i][j-1][k-1][l]),max(f[i-1][j][k][l-1],f[i][j-1][k][l-1])) f[i][j][k][l]=max(max(f[i1][j][k1][l],f[i][j1][k1][l]),max(f[i1][j][k][l1],f[i][j1][k][l1]))
+ a [ i ] [ j ] + a [ k ] [ l ] − ( ( i = = k & j = = l ) ? a [ i ] [ j ] : 0 ) +a[i][j]+a[k][l]-((i==k\&j==l)?a[i][j]:0) +a[i][j]+a[k][l]((i==k&j==l)?a[i][j]:0)
那么最终答案很明显就是 f [ n ] [ m ] [ n ] [ m ] f[n][m][n][m] f[n][m][n][m]


但是这样的时空复杂度太不理想了,所以可以考虑剪枝。
我们发现,如果走了 s u m sum sum步,横坐标为 x x x,那么很明显纵坐标就是 s u m − x sum-x sumx。因为只能往下或右走。那么我们就可以想到另一种方法:
f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示走了 i i i,两条路的横坐标分别是 j j j k k k时的最大权值和。
这样我们就可以算出两条路的纵坐标,进而进行转移。
方程如下:
f [ i ] [ j ] [ k ] = m a x ( m a x ( f [ i − 1 ] [ j ] [ k ] , f [ i − 1 ] [ j − 1 ] [ k − 1 ] ) , m a x ( f [ i − 1 ] [ j ] [ k − 1 ] , f [ i − 1 ] [ j − 1 ] [ k ] ) ) f[i][j][k]=max(max(f[i-1][j][k],f[i-1][j-1][k-1]),max(f[i-1][j][k-1],f[i-1][j-1][k])) f[i][j][k]=max(max(f[i1][j][k],f[i1][j1][k1]),max(f[i1][j][k1],f[i1][j1][k]))
+ a [ j ] [ i − j + 1 ] + a [ k ] [ i − k + 1 ] − ( j = = k ? a [ j ] [ i − j + 1 ] : 0 ) +a[j][i-j+1]+a[k][i-k+1]-(j==k?a[j][i-j+1]:0) +a[j][ij+1]+a[k][ik+1](j==k?a[j][ij+1]:0)
那么最终答案就是 f [ n + m − 1 ] [ n ] [ n ] f[n+m-1][n][n] f[n+m1][n][n]
这样时间复杂度就变成了 O ( n 3 + n m ) O(n^3+nm) O(n3+nm)


题外话
这道题还可以用网络流做。
摘自 https://www.luogu.org/blog/user21682/solution-p1006
在这里插入图片描述


代码:

O ( n 2 m 2 ) O(n^2m^2) O(n2m2)

#include <cstdio>
#include <iostream>
using namespace std;
int a[51][51],f[51][51][51][51],m,x,y,n,k;

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
     for (int j=1;j<=m;j++)
      scanf("%d",&a[i][j]);
    for (int i=1;i<=n;i++)
     for (int j=1;j<=m;j++)
      for (int q=1;q<=n;q++) 
       for (int p=1;p<=m;p++)
       {
          f[i][j][q][p]=max(f[i-1][j][q-1][p],max(f[i][j-1][q-1][p],max(f[i-1][j][q][p-1],f[i][j-1][q][p-1])))+a[i][j]+a[q][p];
          if(i==q&&j==p)f[i][j][q][p]-=a[i][j];
       }
    printf("%d",f[n][m][n][m]);
    return 0;
}

O ( n 3 + n m ) O(n^3+nm) O(n3+nm)

#include <cstdio>
#include <iostream>
#define N 60
using namespace std;

int n,m,f[N+N][N][N],a[N][N];

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
     for (int j=1;j<=m;j++)
      scanf("%d",&a[i][j]);
    for (int i=1;i<n+m;i++)
     for (int j=1;j<=n;j++)
      for (int k=1;k<=n;k++)
       if (i-j+1>0&&i-k+1>0)
       {
       		f[i][j][k]=f[i-1][j][k];
       		f[i][j][k]=max(f[i][j][k],f[i-1][j-1][k-1]);
       		f[i][j][k]=max(f[i][j][k],f[i-1][j][k-1]);
       		f[i][j][k]=max(f[i][j][k],f[i-1][j-1][k]);
       		f[i][j][k]+=a[j][i-j+1]+a[k][i-k+1];
       		if (j==k) f[i][j][k]-=a[j][i-j+1];
       }
    printf("%d\n",f[n+m-1][n][n]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值