POJ 3279(二进制枚举+简单搜索)

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

 

题目大意:给一个面,1代表黑色,0代表白色,翻一个牌的同时,会把跟他有相邻边的牌一起翻了,问你如何才能在翻最少牌的情况下把他变成全白,如果有多种情况那么输出字典序最小的。

 

题目思路:对于一行来说,假如他有m个数,那么很明显,这一行一共有2^m种翻转方案。所以我们很显然的想出,可以用0~2^m-1的十进制数,转换成二进制,来代表每一种翻转方案。代码中(i>>j)&1就是来将十进制转换成二进制。然后对于每种翻转情况,很清楚的可以看到,如果确定了第一行,那么后面就能确定。因为第一行已经确定了,那么为了把第一行的黑色变成白色,唯一的方案就是翻转这个黑色块下面的那个牌,所以我们要做的就是,从第二行开始,然后每个都判断一下它上面的是不是黑色,如果是黑色,那么就翻自己,temp[i][j]=1,这个temp是用来记录翻转的方案。那么如何判断上面是不是黑色呢?很简单,简单搜索一下,这里很巧妙的一种思路,首先是自己对应的颜色,+前后左右自己的翻转次数,对2进行取模,如果是0就说明是白色,1就说明是黑色。全部解决完以后,判断一下最后一行的颜色,如果最后一行全是白色的,那么恭喜这个情况是可以的,如果最后一行存在黑块,那么只能凉凉了,因为没有下一行能帮忙了。然后就是判断一下这种情况下翻了几次,如果翻得比当前ans存的小,那么就把这个作为ans,复制到opt里,opt作为最优解,由于我们是从0~2^m-1枚举翻转情况,而且只有在num<ans的时候复制,所以只取最前面的num最小的,直接就能获得字典序最小的情况。

 

以下是代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
#define inf 0x3f3f3f3f
#define MAXN 20
#define rep(i,a,b) for(int i=a;i<=b;i++)
int n,m,map1[MAXN][MAXN],temp[MAXN][MAXN],opt[MAXN][MAXN];
int dir[5][2]={{1,0},{-1,0},{0,0},{0,1},{0,-1}};
int judge(int x,int y){
    int temp2=map1[x][y];
    for(int i=0;i<5;i++){
        int xx=x+dir[i][0],yy=y+dir[i][1];
        if(1<=xx&&xx<=n&&1<=yy&&yy<=m){
            temp2+=temp[xx][yy];
        }
    }
    return temp2&1;
}
int cal(){
    rep(i,2,n){
        rep(j,1,m){
            if(judge(i-1,j)){
                temp[i][j]=1;
            }
        }
    }
    rep(i,1,m){
        if(judge(n,i))return -1;
    }
    int num=0;
    rep(i,1,n){
        rep(j,1,m){
            if(temp[i][j])num++;
        }
    }
    return num;
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        rep(i,1,n){
            rep(j,1,m){
                scanf("%d",&map1[i][j]);
            }
        }
        int ans=inf,num;
        rep(i,0,(1<<m)-1){
            memset(temp,0,sizeof(temp));
            rep(j,0,m-1){
                temp[1][m-j]=(i>>j)&1;
            }
            num=cal();
            if(num>=0&&num<ans){
                ans=num;
                memcpy(opt,temp,sizeof(temp));
            }
        }
        if(ans==inf)printf("IMPOSSIBLE\n");
        else{
            rep(i,1,n){
                rep(j,1,m){
                    printf("%d%c",opt[i][j],j==m?'\n':' ');
                }
            }
        }
    }
    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值