[Usaco2007 Open]Fliptile 翻格子游戏 状压dp

n,m<=15,直接搞肯定不行,考虑一行一行来,

每一行的状态只与三行有关,所以从第一行开始枚举,每一次让下面一行填上他上面那行的坑

最后一行必须要同时满足他自己和他上面那行,否则舍去

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
int n,m,a[18][18],now[18][18],tim[18][18],nxt[18],final[18],num,tot;
bool bo=0;
int getnum(int x){
    int ans=0;
    while(x){
        ans+=x&1;
        x>>=1;
    }
    return ans;
}
void dfs(int step,int x){
    num+=getnum(x);
    for(int i=m-1;i>=0;i--){
        if(x&(1<<i)){
            now[step][i]^=1;
            if(i<m-1) now[step][i+1]^=1;
            if(i>0) now[step][i-1]^=1;
            if(step<n) now[step+1][i]^=1;
        }
    }
    nxt[step+1]=0;
    for(int i=m-1;~i;i--)
        nxt[step+1]|=now[step][i]*(1<<i);
    if(step==n){
        for(int i=m-1;~i;i--)
            if(now[n][i])
                return;
        if(num>tot) return;
        bo=1; tot=num;
        for(int i=1;i<=n;i++)
            final[i]=nxt[i];
        return;
    }
    dfs(step+1,nxt[step+1]);
}
int main()
{
    scanf("%d%d",&n,&m);
    tot=100000;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    for(int i=(1<<m)-1;~i;i--){
        num=0;
        for(int j=0;j<m;j++)
            for(int k=1;k<=n;k++)
                now[k][j]=a[k][m-j];
        nxt[1]=i;
        dfs(1,i);
    }
    if(!bo){
        printf("IMPOSSIBLE\n");
        return 0;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)
            printf("%d ",(final[i]>>(m-j))&1);
        printf("\n");
    }
    return 0;
}


posted @ 2017-08-07 06:20  Ren_Ivan  阅读(184)  评论(0编辑  收藏  举报