poj2411 Mondriaan's Dream 状压dp

poj2411 Mondriaan’s Dream
题目大意: 1*2的方砖铺n*m的地面。n,m<=11
题解:dp[i,state]表示前i行铺完(不是铺满) 第i行状态为state的方案数
state为一个二进制数 1表示铺了 0表示没铺
显然 dp[i,state]=dp[i-1,state’]; state和state’兼容
什么情况state和state’兼容呢
1.第i行的第j列为1,第i-1行的第j列为1,这样的话,说明第i行的第j列一定不是竖放而是横放否则会与第i-1行的第j列冲突
所以马上紧接着判断第i行第j+1列,如果是1,那么满足横放的规则,同时也要第i-1行第j+1列也要为1,否则的话这个格子没办法填充,
成立后向左移动两格
不满足上述条件的,就是两个不兼容或者不合法的状态
2.第i行第j列为1,第i-1行第j列为0,那么说明第i行第j列应该竖放并填充第i-1行第j列,成立后向左移动一格
3.第i行第j列为0,说明不放方块,那么第i-1行第j列必须为1,否则没法填充这个格子。若第i-1行第j列也为0,不兼容不合法
(至于第i行第j列这个格子空着干什么,其实就是留出来给第i+1行竖放的时候插进来的)
0 ->
1 -> 竖着放

1 1 ->
1 1 ->横着放

1 ->
0 ->不放

但是,还可以优化(?)
朴素的做法是,枚举i的state 枚举i-1的state 判断是否兼容 复杂度O(2^(2n)*n^2) 我X居然还过了QAQ
机智的做法是,初始dfs预处理出哪些状态兼容 建图一样建边
复杂度是O(2^(2n)) 上一个复杂度理论真的过不了啊QAQ

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=11;
struct E {int to,nxt;}edge[1<<(2*N)];
int tot=1,idx[1<<N|10];
int n,m;
ll dp[N+1][1<<N|10];
void addedge(int from,int to){
    //printf("%d %d\n",from-1,to-1);
    edge[tot].to=to;edge[tot].nxt=idx[from];idx[from]=tot++;
}
//第i列 s1->s2 s2可到达s1 
void dfs(int i,int s1,int s2){
    if(i>m) return;
    if(i==m) addedge(s1+1,s2+1);
    dfs(i+1,(s1<<1),(s2<<1)|1);
    dfs(i+1,(s1<<1)|1,(s2<<1));
    dfs(i+2,(s1<<2)|3,(s2<<2)|3);
}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    while(true){
        scanf("%d%d",&n,&m);
        if(n==0 && m==0) break;
        if(n*m%2) {printf("0\n");continue;}//n行m列 
        memset(dp,0,sizeof(dp));dp[0][(1<<m)-1]=1;
        memset(idx,0,sizeof(idx));tot=1;
        dfs(0,0,0);
        for(int i=1;i<=n;i++)
            for(int j=0;j<(1<<m);j++)
                for(int k=idx[j+1];k;k=edge[k].nxt)
                    dp[i][j]+=dp[i-1][edge[k].to-1];
        printf("%lld\n",dp[n][(1<<m)-1]);
    }
    return 0;
}

你可能感兴趣的:(dp,poj,状压dp)