• 状压dp Mondriaan's Dream poj2411


    超经典的一道题目,实现这题的方法也有非常多种

    1.利用DFS建立矩阵,然后通过高速矩阵幂得到答案(运用于min(m,n)比較小。可是max(m,n)很大的情况)

    2.利用dp状压解决


    第一种在我的还有一篇博客里有

    http://blog.csdn.net/qwb492859377/article/details/47138821


    我们在这里讨论另外一种方法。

    刘汝佳的算法训练指南中384页介绍了一种复杂度O(mn*2^m)的方法,这个复杂度实在是太小了。

    我对着上面敲结果仅仅用了32ms就过了这题,然后花了非常久才看懂(蒟蒻)


    他的状态并非一整行一整行的。。所以一開始我就陷入了误圈。

    第二,,他代码里有个1<<m,我一直没搞清楚。究竟他的下标是从0開始还是从1開始,怎么会出现1<<m呢。事实上这是上一次的1<<m-1。由于他把状态先左移过一次-_-....


    我依据我的理解讲下代码

    (i,j)表示以这个格子为右下角,要不要放骨牌以及放那种骨牌. k表示在(i,j)之前的连续m个方块的状态,0表示没放,1表示已经放了,当中靠近(i,j)的下标是0


    先枚举,i再枚举j,最后枚举k,也就是红色表示的区域

    假设(i,j)不放。那么它上面那一个就必须已经放过了,换句话说,仅仅有k&(1<<(m-1)) 。k才有资格将状态转移到新的状态(k<<1)^(1<<m)

    这个状态就是左移后。把曾经的第一位去掉。然后在最后面补上一个0,相当于红色区域所有向右挪了一个单位。

    假设(i,j)放竖直的骨牌,那么i不能为0,且!k&(1<<(m-1))满足。才干考虑把k状态转移到新的状态k << 1 | 1去

    假设(i,j)放横向的骨牌,那么j不能为0,且!(k&1)&&(1<<(m-1)) 也就是k的最后一个要是空着的。并且(i,j)上面一个必须是1。这样才干横向放后上面的不会存在空着的。

    刚開始我还在思考,应该(i-1,j-1)也应该要是1才干够把。,可是后来想了一下。那个位置一定会是1,否则(i,j)左边那个位置就不可能是0了。由于仅仅能通过第一种情况转移过来

    所以当满足这些条件时,k能转移到新状态(k<<1)^(1<<m)|3上来


    语言表达能力有限,,,来看代码把,,感觉刘汝佳那份代码尽管写起来非常工整清晰。反而有些地方变得非常难理解了

    然后dp利用滚动数组,得到的状态向后更新答案的方式,就能攻克了


    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<vector>
    #include<functional>
    #include<algorithm>
    
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    
    const int MX = 3000 + 5;
    const int INF = 0x3f3f3f3f;
    
    int m, n;
    LL dp[2][MX];
    
    int main() {
        //freopen("input.txt","r",stdin);
        while(~scanf("%d%d", &m, &n), m + n) {
            memset(dp, 0, sizeof(dp));
            if(m > n) swap(m, n);
    
            int cur = 0, nxt = 1;
            dp[cur][(1 << m) - 1] = 1;
    
            for(int i = 0; i < n; i++) {
                for(int j = 0; j < m; j++) {
                    memset(dp[nxt], 0, sizeof(dp[nxt]));
    
                    for(int k = 0; k < (1 << m); k++) {
                        if(k & (1 << (m - 1))) dp[nxt][(k << 1) ^ (1 << m)] += dp[cur][k];//不放
                        if(i && !(k & (1 << (m - 1)))) dp[nxt][k << 1 | 1] += dp[cur][k];//竖着放
                        if(j && !(k & 1) && (1 << (m - 1))) dp[nxt][(k << 1) ^ (1 << m) | 3] += dp[cur][k];//横着放
                    }
                    swap(cur, nxt);
                }
            }
            printf("%I64d
    ", dp[cur][(1 << m) - 1]);
        }
        return 0;
    }
    


  • 相关阅读:
    标识类型模式
    脏检查和快照的故事
    Hibernate入门案例及增删改查
    Oracle函数解析
    Oracle基础及三层分页查询
    java解析xml文档(dom)
    TCP和UDP的聊天
    java线程同步
    试题分析(第三套)
    java线程的简单实现及方法
  • 原文地址:https://www.cnblogs.com/lytwajue/p/7283151.html
Copyright © 2020-2023  润新知