poj 3279 Fliptile 二进制枚举

题目链接:http://poj.org/problem?id=3279

此题在kuangbin带你飞专题一做的,开始把它叫做母牛翻转问题,但是第一次基本上都是按着别人的代码写的,现在又在理解的基础上把它重新敲了一遍。

思路:枚举第一行的翻转状态,然后根据翻转要求,要使第一行剩下的1变成0,则只能翻转第二行来使第一行余下的1变成0,且只能是正下方相邻的那一个。这样依次往下翻转每一行,最后是判断最后一行翻转后是不是全部为0即可。由于每个点翻转奇数次的效果相同,每个点翻转偶数次效果相同,所以要使翻转次数最小,则每个点只能翻或者不翻,这就是第一行的翻转状态能用二进制来枚举的原因。

不解之处:题目要求是小翻转次数和相应的字典序,这里只是找到解就输出了,为什么这样一定是满足要求的呢?


代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define LL __int64
#define INF 0xffffffff

using namespace std;

int A[20][20],vis[20][20],B[20][20];
int M,N;

int d[][2]={1,0,-1,0,0,1,0,-1,0,0};
int isok(int s){
    memset(vis,0,sizeof(vis));
    memcpy(B,A,sizeof(B));//把A数组拷贝给B数组
    for(int i=0;i<N;i++)//枚举第一行的翻转状态
        if(s&(1<<i)){//该位置为1,则翻转改点及其四周的点
            B[0][i]^=1;B[1][i]^=1;vis[0][i]=1;
            if(i>0) B[0][i-1]^=1;
            if(i<N-1) B[0][i+1]^=1;
        }
    for(int i=1;i<M;i++)//这里道理同上
        for(int j=0;j<N;j++)
            if(B[i-1][j]){
                vis[i][j]=1;
                for(int p=0;p<5;p++){
                    int x=d[p][0]+i,y=d[p][1]+j;
                    if(x<0 || y<0 || x>=M || y>=N) continue;
                    B[x][y]^=1;
                }
            }
    for(int i=0;i<N;i++) if(B[M-1][i]) return 0;//判断最后一行是不是全为0
    return 1;
}



int main(){
    //freopen("D:\\in.txt","r",stdin);
    while(cin>>M){
        cin>>N;
        for(int i=0;i<M;i++) for(int j=0;j<N;j++)
            scanf("%d",&A[i][j]);
        int op=0;
        for(int i=0;i<(1<<N);i++)//枚举第一行的翻转状态,用一个整数表示
            if(isok(i)){//判断是否有解
                op=1;break;
            }
        if(op){
            for(int i=0;i<M;i++) for(int j=0;j<N;j++)
                cout<<vis[i][j],j==N-1? cout<<endl:cout<<" ";
        }
        else cout<<"IMPOSSIBLE\n";
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值