POJ -3279 Fliptile (二进制转换,枚举每行)

来源:互联网 发布:博途v13编程软件下载 编辑:程序博客网 时间:2024/05/22 08:06

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

题意就是m*n的棋盘,每一个位置摁下,自己和周围四个位置都会发生翻转,问你能不能通过最下的次数把整张棋盘全部转换为白色,并且要去摁下坐标的位置的字典序最小(比如
0 0 0 1
0 0 0 1
0 0 0 0
0 0 0 0
就是将其看成一行字符串,要求答案如果次数相同时,输出字典序更小的那个答案。)

思路就是,在不考虑第一行的情况下,从第二行开始枚举,如果它上一行的当前为位置是否为1,如果为1就摁下当前这个位置。一直枚举到最后一行,如果最后一行也全部翻转到白色,就表示这个方案可以达到目的。然后再比较这个方案是否是最优的,不过这是不考虑第一行的情况,对于第一行我们仍然要枚举,每一个棋子都有摁下或不嗯下两种情况,就有2^m种可能。

代码如下:

#include<iostream>#include<cstdio>#include<cmath>#include<string>#include<cstring>#include<algorithm>using namespace std;struct node{    string str;    int time;}mi;//用来记录答案const int INF = 0x7fffffff;const int MAX = 20;bool Map[MAX][MAX];bool opt[MAX][MAX];bool used[MAX][MAX];const int xx[] = {-1,0,1,0};const int yy[] = {0,-1,0,1};int n,m;void GetMapInfo(){    cin >> n >> m;    for(int i=1;i<=n;i++){        for(int j=1;j<=m;j++){            cin >> Map[i][j];        }    }}//读取整张地图的信息bool judge(int x,int y){    if(x < 1 || y < 1 || x > n || y > m)    return false;    return true;}//判断这个坐标是否越界void ChangeMap(int x,int y){    int nex,ney;    Map[x][y] = Map[x][y]^1;    for(int i=0;i<4;i++){        nex = x + xx[i];        ney = y + yy[i];        if(!judge(nex,ney)) continue;        Map[nex][ney] = Map[nex][ney]^1;    }}//当这个棋子摁下去时,周围棋子和该棋子会翻转。bool isok(){    for(int i=2;i<=n;i++){        for(int j=1;j<=m;j++){            if(Map[i-1][j] == true){                ChangeMap(i,j);                opt[i][j] = true;            }        }    }    int sum = 0;    for(int j=1;j<=m;j++)        sum += Map[n][j];    if(sum == 0)    return true;    return false;}//判断是否可以使整个棋盘全部变为白棋void Changeopt(int x){    int r = m;    while(x > 0){        if(x & 1){            opt[1][r] = true;            ChangeMap(1,r);        }        x >>= 1;        r--;    }}//改变第一行的情况.比如3就代表第一行嗯下的是0011。然后对对应的坐标进行操作node GetNode(){    node a;    a.str = "";a.time = 0;    for(int i=1;i<=n;i++){        for(int j=1;j<=m;j++){            if(opt[i][j] == 1)  a.time++;            char ch = '0' + opt[i][j];            a.str += ch;        }    }    return a;}//将摁下坐标的情况转换成字符串,并记录摁下的次数void outans(){    int Count = 0;    for(int i=1;i<=n;i++){        for(int j=1;j<=m;j++){            opt[i][j] = mi.str[Count++] - '0';        }    }    for(int i=1;i<=n;i++){        cout << opt[i][1];        for(int j=2;j<=m;j++)            cout << " " << opt[i][j];        cout << endl;    }}//输出这个答案,即摁下坐标的情况.bool solve(){    GetMapInfo();    bool Isfind = false;    bool Map_back[MAX][MAX];    memcpy(Map_back,Map,sizeof(Map));//因为每次都要重置地图,所以我们要把地图保存。    //memcpy()就是拷贝二位数组    mi.time = INF;    for(int i=0;i<=pow(2,m)-1;i++){//改变第一行,因为第一行嗯下的情况只有2^m种,可以把对应二进制转换成数来存储。        memcpy(Map,Map_back,sizeof(Map_back));//重置这张地图        memset(opt,false,sizeof(opt));//把摁下坐标清空        Changeopt(i);//第一行坐标改变情况        if(isok()){            Isfind = true;            node x = GetNode();            if(x.time < mi.time)//用mi来记录最小的摁下次数,和字典序较小的。                mi = x;            else if(x.time == mi.time && x.str < mi.str)                mi = x;        }    }    if(Isfind)  return true;    return false;}int main(void){    if(solve()){  //      cout << mi.str << endl;        outans();    }    else        cout << "IMPOSSIBLE" << endl;    return 0;}