zoukankan      html  css  js  c++  java
  • POJ-2411 Mondriann's Dream (状压DP)

    求把(N*M(1le N,M le 11)) 的棋盘分割成若干个(1 imes 2) 的长方形,有多少种方案。例如当 (N=2,M=4)时,共有5种方案。当(N=2,M=3)时,有3种方案。

    NM只有11,八九不离十可以状压了,反正得挨个铺,所以从上到下考虑。假如现在铺好了前(i) 层,基本思想就是从(i) 层的状态转移到(i+1)层的状态。但是该如何表示?观察一下铺满第 (i) 层的样子(必须保证第(i)层是满的,也就是说有的可以凸出来到(i+1)层但是要保证(i)层是满的)

    对于第 i 行中竖着放的,第 (i+1) 层要受到牵连,它必须补全竖着放置的上一半才行。但对于横着放的,第(i+1)层则无所谓。
    所以我们可以用二进制中的 1 来表示他是否是竖着放置的上一半。为0则为其他状况。
    (d[i][j])表示第 (i) 的形态为(j) 时,前(i) 行分割方案的总数。 (j) 是用十进制整数记录的 (m) 位二进制数。考虑(i+1)行的状态(k)在满足什么情况下转移是合法的。

    • (j)中为 1 的位,(k)中必须为0
    • (j)中为 0 的位,(k)中可以为1,但 k 要是为 0,就必须是连续的偶数个0(想一想为什么)
      对于第一条,可以用 (i&j = 0) 来判断,对于第二条,有(z = i|j),那么 z 的二进制表示中,每一段连续的 0 都必须有偶数个。(这些0代表若干个横着的 (1 imes 2) 长方形,奇数个0无法分割成这种形态。
    #include <iostream>
    #include <cstdio>
    using namespace std;
    int n,m;
    long long f[12][1<<11];
    bool in_s[1<<11];
    
    int main(){
        while(cin>>n>>m && n){
            //先把合法状态筛出来,即二进制表示中每一段连续的0都有偶数个
            for(int i=0;i<1<<m;i++){
                bool cnt = 0,has_odd = 0;
                for(int j=0;j<m;j++)
                    if(i >> j & 1)has_odd |= cnt,cnt=0;
                    else cnt ^= 1;
                in_s[i] = (has_odd | cnt) ? 0 : 1;
            }
            f[0][0] = 1;
            for(int i=1;i<=n;i++){
                for(int j=0;j<1<<m;j++){
                    f[i][j] = 0;
                    for(int k=0;k< 1<<m;k++){
                        if((j & k) == 0 && in_s[j|k])
                            f[i][j] += f[i-1][k];
                    }
                }
            }
            cout<<f[n][0]<<endl;
        }
        return 0;
    }
    
  • 相关阅读:
    各种计算
    C# 杂货
    Unity String格式化字符串
    unity 窗口化运行时取消边框
    WPF开机自启
    wpf 如何让当前窗口隐藏
    Unity 安卓杂货
    unity的一些杂货
    C# 项目中遇到过的坑(持续更新)
    C# 并入UI线程
  • 原文地址:https://www.cnblogs.com/1625--H/p/11268252.html
Copyright © 2011-2022 走看看