• NOIP2008 传纸条(DP及滚动数组优化)


    传送门

    这道题有好多好多种做法呀……先说一下最暴力的,O(n^4的做法)

    我们相当于要找两条从左上到右下的路,使路上的数字和最大。所以其实路径从哪里开始走并不重要,我们就直接假设全部是从左上出发的好啦。设dp[i][j][p][q]表示第一条路枚举到点(i,j),第二条路枚举到点(p,q)时,当前能取到的最大值。

    这样dp方程很显然,就是dp[i][j][p][q] = max(dp[i-1][j][p-1][q],dp[i][j-1][p-1][q],sp[i-1][j][p][q-1],dp[i][j-1][p][q-1]) + a[i][j] + a[p][q].如果i==p && j == q,那么减去一个a[i][j].最后dp[m][n][m][n]即为结果。

    四层循环枚举,复杂度O(n^4),上一下代码。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define rep(i,a,n) for(ll i = a;i <= n;i++)
    #define per(i,n,a) for(ll i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    const int M = 105;
    typedef long long ll;
    
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '-') op = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            ans *= 10;
            ans += ch - '0';
            ch = getchar();
        }
        return ans * op;
    }
    int m,n,dp[55][55][55][55],a[55][55];
    int main()
    {
        m = read(),n = read();
        rep(i,1,m)
        rep(j,1,n) a[i][j] = read();
        rep(i,1,m)
        rep(j,1,n)
        rep(p,1,m)
        rep(q,1,n) 
        {    
            dp[i][j][p][q] = max(max(dp[i-1][j][p-1][q],dp[i-1][j][p][q-1]),max(dp[i][j-1][p-1][q],dp[i][j-1][p][q-1])) + a[i][j] + a[p][q];
            if(i == p && j == q) dp[i][j][p][q] -= a[i][j];
        }
        printf("%d
    ",dp[m][n][m][n]);
        return 0;
    }

    之后我们说一下怎么优化。两条路在走的时候,因为每次只能向右或者下走一格,所以两者必然是在同一条斜对角线上的(一条从右上到左下的对角线)那么我们就可以用对角线的横纵坐标和来表示当前DP到了哪里,之后每次只要枚举两个点的横坐标就可以把路径表示出来(也就是表示出来当前枚举的是哪两个点)。这样的话就可以把DP过程的时空降到三维的。

    具体的写法也很简单,直接看一下代码即可。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    using namespace std;
    typedef long long ll;
    const int M = 60;
    int n,m,f[M][M],dp[120][M][M],q[M]; 
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
        if(ch == '-') op = -1;
        ch = getchar();
        }
        while(ch >='0' && ch <= '9')
        {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
        }
        return ans * op;
    }
    
    int main()
    {
        m = read(),n = read();
        rep(i,1,m)
        rep(j,1,n) f[i][j] = read();
        rep(i,2,n+m)
        {
        rep(p,1,i-1)
        {
                if(p > m) break;
            rep(q,1,i-1)
            {
                    if(q > m) break;
            dp[i][p][q] = max(max(dp[i-1][p][q-1],dp[i-1][p-1][q]),max(dp[i-1][p-1][q-1],dp[i-1][p][q])) + f[p][i-p] + f[q][i-q];
            if(p == q) dp[i][p][q] -= f[p][i-p];
            }
        }
        }
        printf("%d
    ",dp[n+m][m][m]);
        return 0;
    }
        

    之后,时间复杂度基本已经不能再优化,不过空间复杂度却可以优化到O(n^2)(其实还要再乘以一个常数)

    我们从刚才的三维DP的转移过程来考虑一下。每次DP都是从上一条对角线上的元素转移过来,和其他的对角线没有任何关系,相当于我们在每次DP的时候只需要考虑两个,一个是i,一个是i-1,在i-1之前的其实已经没有了任何作用。所以我们完全可以废物再利用。因为每次i的值只改变1,所以可以使用按位与的方法把它变成0或者1,之后直接dp即可,而最后的答案就是dp[(m+n)&1][m][m].这样空间复杂度又会降一维。本题的数据范围小,如果数据范围较大,滚动数组对于空间的节省就极为有用了。

    看一下代码。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    using namespace std;
    typedef long long ll;
    const int M = 60;
    int n,m,f[M][M],dp[2][M][M]; 
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
        if(ch == '-') op = -1;
        ch = getchar();
        }
        while(ch >='0' && ch <= '9')
        {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
        }
        return ans * op;
    }
    
    int main()
    {
        m = read(),n = read();
        rep(i,1,m)
        rep(j,1,n) f[i][j] = read();
        rep(i,2,n+m)
        {
        rep(p,1,i-1)
        {
            if(p > m) break;
            rep(q,1,i-1)
            {
            if(q > m) break;
            int k = (i-1)&1;
            dp[i&1][p][q] = max(max(dp[k][p][q-1],dp[k][p-1][q]),max(dp[k][p-1][q-1],dp[k][p][q])) + f[p][i-p] + f[q][i-q];
            if(p == q) dp[i&1][p][q] -= f[p][i-p];
            }
        }
        }
        printf("%d
    ",dp[(n+m)&1][m][m]);
        return 0;
    }
  • 相关阅读:
    cell list of blocks physical read等待事件
    cell manager discovering disks等待事件
    OOP
    静态网页与动态网页的区别
    一个HTTP请求的详细过程
    PING 确认网络连接情况
    软件开发模型
    搭建网站的全套流程
    Design Diagram
    网络基础
  • 原文地址:https://www.cnblogs.com/captain1/p/9544466.html
Copyright © 2020-2023  润新知