zoukankan      html  css  js  c++  java
  • [Luogu P1006]传纸条 (网格DP)

    题面

    传送门:https://www.luogu.org/problemnew/show/P1006


    Solution

    挺显然但需要一定理解的网络(应该是那么叫吧)DP

    首先有一个显然但重要的结论要发现:从左上走到右下再从右下走回左上=从左上走两次到右下

    那么接下来可以考虑:

    设f[i][j][k][l]为第一次走到了(i,j)第二次走到了(k,l) 在路径不交错为前提下的能取到的最大友好值

    转移方程也挺好写的

    考虑这种情况能从哪里转移过来就好(i,j)可以从(i-1,j)或(i,j-1)转移过来,(k,l)可以从(k-1,l)或(k-1,l-1)转移过来

    排列组合一下,总共4种可能性,取个最大值再加上a[i][j]和a[k][l]就好

    当然(i==k and j==l) 即两个点重合的情况直接continue,因为f 的意义是之前的不重合,当前的也不能重合

    预处理整个f设为0就好

    时间复杂度O(n^4) n=50,显然能过

    接下来我们可以考虑一个优化

    因为我们两次从左上到右下是一起走的

    就有这么一个推论: i+j = k+l (画个图就好,挺好发现的)

    既然这两个相等,也就意味着我们可以通过总和与i,k推算出j,l

    然后我们的方程就可以优化成这样的:

    f[i][j][k]的意思为:走了i步,第一次走到了第j行,第二次走到了第k行

    它们的橫坐标分别为:i-j+2,i-k+2

    转移同理

    这样,时间复杂度就可以优化为O(n^3),相较之前的可以称为巨大的飞跃

    然后就OjbK了


    Code

    //Luogu P1006 传纸条
    //May,4th,2018
    //网格DP
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    long long read()
    {
        long long x=0,f=1; char c=getchar();
        while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
        while(isdigit(c)){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    const int N=50+5;
    int f[2*N][N][N],a[N][N],n,m;
    int main()
    {
        m=read(),n=read();
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
                a[i][j]=read();
                
        int MAX=n+m;
        for(int i=1;i<=MAX;i++)
            for(int j=1;j<=m;j++)
                for(int k=1;k<=m;k++)
                {
                    int X1=i-j+2,Y1=j,X2=i-k+2,Y2=k;
                    if((X1==X2 and Y1==Y2)==false and X1>0 and X2>0 and X1<=n and X2<=n)
                    {
                        f[i][j][k]=max(f[i][j][k],f[i-1][j][k]);
                        f[i][j][k]=max(f[i][j][k],f[i-1][j-1][k]);
                        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-1]);
                        f[i][j][k]+=a[j][X1]+a[k][X2];
                    }
                }
        
        printf("%d",max(f[m+n-3][m][m-1],f[m+n-3][m-1][m]));
        return 0;
    }
    C++(正解)

    后记

    当时想为什么不会交叉的时候考虑了挺久的,这种类型的网格DP还是得多学习一个,我啊,太naive了

    我太弱了

    自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。
  • 相关阅读:
    用例输入单元测试(3)参数化测试方法
    线程文件[MFC]线程优先级设置,下拉列表框ComboBox,复选框CButton,列表框CListBox的使用
    设置系统SetLocalTime设置系统时间
    页面错误毕业设计之错误集锦(六)
    定义数据类型SQL server中SET ANSI_PADDING对char、varchar、nvarchar的影响
    查找关键字算法:静态查找表(Static Search Table)
    密码注册ASP.NET实现忘记密码
    卡函数or1200基于simplespi的SD卡驱动
    目的地返回POJ 2336 动态规划(DP) Ferry Loading II
    坐标序列hdu 1003 解题报告 Max Sum
  • 原文地址:https://www.cnblogs.com/GoldenPotato/p/8989311.html
Copyright © 2011-2022 走看看