POJ - 3279----Fliptile(枚举,遍历)

Farmer John knows that an intellectually satisfied cow is a happy cow who will give more milk. He has arranged a brainy activity for cows in which they manipulate an M × N grid (1 ≤ M ≤ 15; 1 ≤ N ≤ 15) of square tiles, each of which is colored black on one side and white on the other side.

As one would guess, when a single white tile is flipped, it changes to black; when a single black tile is flipped, it changes to white. The cows are rewarded when they flip the tiles so that each tile has the white side face up. However, the cows have rather large hooves and when they try to flip a certain tile, they also flip all the adjacent tiles (tiles that share a full edge with the flipped tile). Since the flips are tiring, the cows want to minimize the number of flips they have to make.

Help the cows determine the minimum number of flips required, and the locations to flip to achieve that minimum. If there are multiple ways to achieve the task with the minimum amount of flips, return the one with the least lexicographical ordering in the output when considered as a string. If the task is impossible, print one line with the word “IMPOSSIBLE”.

Input
Line 1: Two space-separated integers: M and N
Lines 2.. M+1: Line i+1 describes the colors (left to right) of row i of the grid with N space-separated integers which are 1 for black and 0 for white
Output
Lines 1.. M: Each line contains N space-separated integers, each specifying how many times to flip that particular location.

Sample Input
4 4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1
Sample Output
0 0 0 0
1 0 0 1
1 0 0 1
0 0 0 0

题目大意: 给你一个15*15的黑白棋盘,每次可以翻转一个棋子,与其相邻的4个棋子都会被翻转,求出最少翻转几次可以让棋盘全部变为白棋(即为0),给出翻过的棋子。

思路: 开始准备暴搜,但发现所需的空间太大了,后来想起看过的书上有类似的题,参考了下思路,突然明白,棋盘所有的状态可以全压缩为棋盘第一行的状态,因为第二行的棋子翻不翻可以由上一行决定。

举个例子,第一行不动,如果第二行的某一个棋子的上方为黑色,则要翻这个旗子,这种策略,可以将保证当此行之前的行已经全部翻好,就是说,随着扫描棋子的进行整个棋盘的黑白状态会变成成最后一行的状态,只要最后一行不是全白,就说明第一行此时的状态不能让棋盘变为全白,就应该改变第一行的状态。
故,就应该将第一行的状态全部遍历出来。

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

const int MAX=15;
const int INF=1000;


int M,N;//初始步数 
int A[MAX][MAX],B[MAX][MAX];//B为保存的答案 
int flag[MAX][MAX];//棋子的翻转标记(1-->黑色,0-->白色)  
int dx[5]={-1,0,0,0,1},dy[5]={0,1,0,-1,0};//翻转的五个方向 

//得到棋子的颜色(1为黑,0为白) 
int get(int x,int y){
    int t=A[x][y];
    for(int i=0 ; i<5 ; i++){
        int a=dx[i]+x,b=dy[i]+y;

        if(a>=0 && a<M && b>=0 && b<N){
            //棋子翻偶数次等于没翻 
            t += flag[a][b];
        }
    }
    return t%2;
}

int numturn(void){      
    //从第二行开始,每一行是由上一行来确定的 
    for(int i=1 ; i<M ; i++){
        for(int j=0 ; j<N ; j++){
            //上一行此位置如果是黑色则翻转 
            if(get(i-1,j) == 1){
                flag[i][j]=1;
            }
        }
    }

    //如果最后一行不是全白,则此情况无解 
    for(int i=0 ; i<N ; i++){
        if(get(M-1,i) == 1){
            return INF;
        } 
    }   

    int res=0;//翻转次数
    for(int i=0 ; i<M ; i++){ 
        for(int j=0 ; j<N ; j++){ 
            res += flag[i][j];
        }
    }
    return res; 
}

int main(void){
    cin>>M>>N;
    for(int i=0 ; i<M ; i++){
        for(int j=0 ; j<N ; j++){
            cin>>A[i][j];
        }
    } 
    int step=INF;//翻转步数 
    memset(B,0,sizeof(B));

    //对第一行进行暴力枚举,共2^N种情况
    //0则对应第一行不翻,2^N-1对应第一行全翻 
    for(int i=0 ; i<1<<N ; i++){
        memset(flag,0,sizeof(flag));
        //每一种情况 
        for(int j=0 ; j<N ; j++){
            //位运算得到此种情况的第一行翻转情况
            //0代表未翻为白色1代表翻了一次为黑色 
            flag[0][j] = i>>j&1;
        }

        int tem=numturn();

        //更新翻转的步数 
        if(tem < step){
            step = tem;
            for(int i=0 ; i<M ; i++){
                for(int j=0 ; j<N ; j++){
                    //保存答案 
                    B[i][j] = flag[i][j];
                }
            }
        }
    }

    if(step == INF){
        cout<<"IMPOSSIBLE"<<endl;
    }else{
        for(int i=0 ; i<M ; i++){
            for(int j=0 ; j<N ; j++){
                cout<<B[i][j];
                if(j<N-1){
                    cout<<" ";
                }else{
                    cout<<endl;
                }
            }
        } 
    }


    return 0;
}  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值