【算法学习】插头DP

插头DP

插头DP是状压DP的一种,又称轮廓线DP,通常解决平面状态压缩问题每个位置的取值都只与相邻的几个位置有关,适用于超小数据范围,网格图,连通性等问题。

插头: 一个格子在某个方向与相邻的格子相连,这些连接的位置就叫插头。
轮廓线: 按从上到下,从左到右的顺序处理,确定状态和未确定状态之间的分界线叫轮廓线。

例题: 蒙德里安的梦想
对于本题,状态的转移方式有三种
不放置:
在这个位置放置插头,则上一行一定不能再有插头,否则不能放满。
image
放置:
横放:
j-1位置必有插头
image
竖放:
上一行必有插头
image

int n,m;
//插头DP,插头只有向下和向右,不放置也就是留下插头为1,放置为0
ll pre[1<<12],ne[1<<12];
void solve(){
    pre[0]=1;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            for(int S=0;S<(1<<m);S++){
                if((S>>j)&1){//不放置,相当于留一个插头
                    ne[S]=pre[S&~(1<<j)];//上一行第j列一定要为0,他的方案数就是上一行j列为0的方案数
                }
                else{
                    ll tmp=0;
                    //放置向右的插头,他的方案数等于j-1为1的方案数
                    if(j-1>=0&&!((S>>(j-1))&1))tmp+=pre[S|1<<(j-1)];
                    //放置向下的插头,他的方案数等于上一行j为1的方案数
                    if(i-1>=0)tmp+=pre[S|1<<j];
                    ne[S]=tmp;
                }
            }
            swap(pre,ne);//滚动
        }

    }
    printf("%lld\n",pre[0]);
}
void init(){
    memset(pre,0,sizeof pre);
    memset(ne,0,sizeof ne);
}
int main(){
    while(cin>>n>>m&&(n||m)){
        init();
        solve();
    }
    return 0;
}
posted @ 2021-09-10 18:29  qingyanng  阅读(350)  评论(1编辑  收藏  举报