1. 程式人生 > >【洛谷P1006】傳紙條【DP】

【洛谷P1006】傳紙條【DP】

題目大意:

題目連結:https://www.luogu.org/problemnew/show/P1006
給出一個 n × m n\times m 的矩陣,每個格子中有權值,求從 ( 1

, 1 ) (1,1) ( n , m )
(n,m)
的兩條不相交路徑使得路徑權值之和最大。


思路:

很明顯可以把題目看成兩條不相交的路徑從 ( 1 , 1 ) (1,1)

( n , m ) (n,m) 。那麼就可以設 f [ i ] [ j ] [ k ] [ l ] f[i][j][k][l] 表示第一條路走到 ( i , j ) (i,j) ,第二條路走到 ( 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]))
+ 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]


但是這樣的時空複雜度太不理想了,所以可以考慮剪枝。
我們發現,如果走了 s u m sum 步,橫座標為 x x ,那麼很明顯縱座標就是 s u m x sum-x 。因為只能往下或右走。那麼我們就可以想到另一種方法:
f [ i ] [ j ] [ k ] f[i][j][k] 表示走了 i i ,兩條路的橫座標分別是 j j 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]))
+ 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)
那麼最終答案就是 f [ n + m 1 ] [ n ] [ n ] f[n+m-1][n][n]
這樣時間複雜度就變成了 O ( n 3 + n m ) O(n^3+nm)


題外話
這道題還可以用網路流做。
摘自 https://www.luogu.org/blog/user21682/solution-p1006
在這裡插入圖片描述


程式碼:

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

#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)

#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++