POJ 2411 状压DP经典

时间:2023-04-27 09:08:50
Mondriaan's Dream
Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions: 16771   Accepted: 9683

Description

Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series' (where he had to use his toilet paper to draw on, for all of his paper was filled with squares and rectangles), he dreamt of filling a large rectangle with small rectangles of width 2 and height 1 in varying ways.
POJ 2411 状压DP经典

Expert as he was in this material, he saw at a glance that he'll
need a computer to calculate the number of ways to fill the large
rectangle whose dimensions were integer values, as well. Help him, so
that his dream won't turn into a nightmare!

Input

The
input contains several test cases. Each test case is made up of two
integer numbers: the height h and the width w of the large rectangle.
Input is terminated by h=w=0. Otherwise, 1<=h,w<=11.

Output

POJ 2411 状压DP经典For
each test case, output the number of different ways the given rectangle
can be filled with small rectangles of size 2 times 1. Assume the given
large rectangle is oriented, i.e. count symmetrical tilings multiple
times.

Sample Input

1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0

Sample Output

1
0
1
2
3
5
144
51205

Source

第一次做状压题目,没怎么优化就是暴力递推。
对于每个格子我们有这么两种可能: 
第一,这个格子被竖着的砖块覆盖
第二,这个格子被横着的砖块覆盖
我们不妨用0/1来表示这个状态,0表示这是一个竖着放置的砖块的上方部分,1表示横向覆盖或者被上方竖着覆盖。
之所以用0表示竖放的上方是因为竖着放会对下一行的放置造成影响,而横着放显然不会,我们在放置下一行的时候需要知道上一行的状态才可,
所以遇见上一行这个位置是0就表示这个格子已经被覆盖了的状态,利用这个来判断冲突放置。
(显然每个格子都要被覆盖,我们如果用01表示覆盖与非覆盖的状态显然是无意义的,要从放置方式着手,这一点当时想了好久= =)
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
LL dp[2][2050];
LL ans[15][15];
bool check(int x,int i)
{  return x&(1<<i); }
bool comp(int A,int B,int N)
{
 int i=0,j,k;
 while(i<N){
    if(!check(A,i)){
        if(!check(B,i)) return 0;
        i++;
    }
    else{
        if(!check(B,i)) i++;
        else {
            if(i==N-1||!check(A,i+1)||!(check(A,i+1)&&check(B,i+1))) return 0;
            else i+=2;
        }
    }
 }
 return 1;
}
void solve(int N,int M)
{
 if(ans[N][M]+1) {printf("%lld\n",ans[N][M]);return;}
 memset(dp,0,sizeof(dp));
 dp[0][(1<<N)-1]=1;
 int cur=1;
 for(int i=1;i<=M;++i){
    for(int j=0;j<(1<<N);++j){dp[cur][j]=0;
        for(int k=0;k<(1<<N);++k){
            if(comp(j,k,N)) dp[cur][j]+=dp[cur^1][k];
        }
    }
    cur^=1;
 }
 ans[N][M]=ans[M][N]=dp[cur^1][(1<<N)-1];
 printf("%lld\n",dp[cur^1][(1<<N)-1]);
}
int main()
{
    memset(ans,-1,sizeof(ans));
    int N,M,i,j,k,l;
    while(scanf("%d%d",&N,&M)!=EOF&&(N||M)){
        if(N*M%2==1) {puts("0");continue;}
        if(N>M) {swap(N,M);}
        solve(N,M);  //M行N列
    }
    return 0;
}