[題解/狀壓dp]POJ_2411_Mondriaan's dream

关于“我读过很多书,到后来大部分都被我忘记了,那阅读的意义是什么?”的疑问,我看过最巧妙的一个回答:当我还是个孩子的时候,我吃过很多的食物,大部分已经一去不复返而且被我忘记了,但可以肯定的是,它们中的一部分已经成长为我的骨头和肉。阅读对你的思想的改变也是如此。


填充的方法:

每個點三種情況,

1)是一個豎著的1*2的長方形的上半部分,對下一行限制是必須補全該長方形,用二進制1來表示

2)是一個豎著的1*2的長方形的下半部分,對下一行無限制,用0表示

3)是一個橫著的1*2的長方形的一部分,對下一行沒有限制,用0表示

按行號dp,f [ i ] [ j ]表示第 i 行狀態為 j 的方案數,

第 i-1 行的狀態 k 能轉移到第 i 行狀態為 j 當且僅當:

1.j 和 k 執行按位与結果為0(保證每個數字1的下方必須是0)

2.j 和 k 執行按位或結果每一段連續的0都必須有偶數個(若干橫著的1*2長方形)

(按位或后就去掉了和上面一起成為一個長方形的情況)

#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
int n,m;
ll f[12][1<<11];
bool b[1<<11];
int main()
{
    while(cin>>n>>m && n){
        //預處理出[0,1<<m-1]中所有每一段連續的0都有偶數個的數 
        for(int i=0;i< 1<<m;i++){
            bool cnt=0,has_odd=0;//cnt為這一段0個數的奇偶性,hasodd為是否為奇數 
            for(int j=0;j<m;j++)
            if(i>>j & 1)
            has_odd|=cnt,cnt=0;//第j位為1就重置,并記錄在hasodd里 
            else cnt^=1;//更新這一段奇偶性 
            b[i]=has_odd|cnt ? 0:1;//其中任何一個是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 && b[j|k])
            f[i][j]+=f[i-1][k];
        }
        printf("%lld
",f[n][0]);
    }
}
原文地址:https://www.cnblogs.com/superminivan/p/10616996.html