zoukankan      html  css  js  c++  java
  • 【洛谷P1006】传纸条【DP】

    题目大意:

    题目链接:https://www.luogu.org/problemnew/show/P1006
    给出一个n×mn imes 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]=max(max(f[i1][j][k1][l],f[i][j1][k1][l]),max(f[i1][j][k][l1],f[i][j1][k][l1]))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]


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


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


    代码:

    O(n2m2)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(n3+nm)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++)
         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
    ",f[n+m-1][n][n]);
        return 0;
    }
    
  • 相关阅读:
    快速击键(MyEclipse编写的QuickHit项目)
    Java语言中的修饰符
    Java中的接口
    建造者模式应用场景
    原型设计模式
    转:fortios 5.4后门植入
    转:json注入
    linux tips
    资料收集
    转:nginx+CGI/FASTCGI
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998465.html
Copyright © 2011-2022 走看看