poj3927

这个是个开关问题,也是个dfs,我一开始看主流题解没看懂怎么回事,后来看了一个写的比较好的才看懂,这题还是挺难的,感觉
主要有几点1:用二进制来模拟那个第一行,1000就是1<<3,然后就是吧
2:如果第一行定下来了,那么下面的就都定下来了,为啥,因为上一行的数,可以用下一行对应的数来翻掉,所以就是说,确定了第一行就行了,然后找这个最佳的翻牌子方法,最后输出,就可以了

#include<cstdio>
#include<cstring>
using namespace std;

int map[20][20] , vis[20][20] , t[20][20];//vis翻转次数,t中间状态
int d[4][2] = {{0,-1},{1,0},{-1,0},{0,1}};
int cnt , N ,M;

void Flip(int i , int j)//翻转i,j
{
    cnt++;//次数加1
    t[i][j] = !t[i][j];//先翻自己
    vis[i][j] = 1;
    for(int k = 0 ; k < 4 ; k++){
        int x = i + d[k][0];
        int y = j + d[k][1];
        if(x < 0 || y < 0 || x >= N || y >= M)
            continue;
        t[x][y] = !t[x][y];//.符合条件就要翻
    }
}
bool judge(int num)//判断状态是否合理
{
    cnt = 0;
    memcpy(t , map , sizeof(t));//给状态
    for(int i = 0 ; i < M ; i++){
        if(num & (1<<i)){//如果某一位为1,那么就翻这个位置
            Flip(0 , i);
        }
    }
    for(int i = 0 ; i < N - 1 ; i++){
        for(int j = 0 ; j < M ; j++){
            if(t[i][j] == 1)//依次,如果上一个是1,那么下一行对应的就该翻了
                Flip(i + 1 , j);
        }
    }
    for(int i = 0 ; i < M ; i++)
        if(t[N-1][i]) return false;//但是最后一行没有下一行来给他翻来转移,所以最后一行一定是0
    return true;
}

int main()
{
    int i , j;
    while(~scanf("%d %d", &N , &M)){
        for(int i = 0 ; i < N ; i++){
            for(int j = 0 ; j < M ; j++)
                scanf("%d",&map[i][j]);
        }
        int step = N * M + 1;
        int p = -1;
        for(int i = 0 ; i < (1 << M ) ;i++){
            if(judge(i) && cnt < step){//枚举第一行
                step = cnt;
                p = i;
            }
        }
        memset(vis , 0 , sizeof(vis));
        if(p >= 0){//找个最小的状态,然后改变
            judge(p);
            for(int i = 0 ; i < N ; i++){
                for(int j = 0 ; j < M ; j++)
                    if(j == M - 1) printf("%d\n",vis[i][j]);
                    else printf("%d ",vis[i][j]);
            }
        }
        else printf("IMPOSSIBLE\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值