//题意:一个4*4的棋盘,没一块儿都是可以反转的,每块儿正反面都由黑色白色组成,没反转一块儿都会影响到自己的上下左右四块儿,任意给一个棋盘样式,问能否在最少有限的次数反转得到全是黑色或者全是白色。与poj2965同一个思路建议先搞懂此题再去看poj2965,点击去博主poj2965
//思路:此题有多解,这里是枚举,首先说明的是整个棋盘对于没一块儿反转次数若为偶数次是没有变化的,奇数次是变化的这个自行理解一下,那么理论最少次数应该是16次,所以那么怎么枚举呢?把这16块儿按照顺序排列的话那么每一种棋盘样式都是唯一的,但是却可以通过不同的反转方法得到,因此,16块儿从组合数学角度整个棋盘样式应该是2^16种,假设我们任意规定白色为1,黑色为0,那么将这整个方格从右至左,从下向上分别为0-3, 4- 7, 8 -11, 11-15按照这个顺序,每位可为0可为1则像16进制的数一样从最小的 0x0~0xffff(0x代表16进制的数)共2^16种情况,所以将这些情况都枚举出来,假设初始状态为全黑则0x0,没反转一位那一位便是1,将题目给的样式当作初始状态,全黑全白当作目标状态,那么初始状态按照所有枚举出的情况进行反转回去,总有能反转成全黑或者全白的反转方法,也就是枚举的2^16 就相当于提供给我们2^16种反转后的样式,我们按照每个样式的反转方法反转回去,看看完成题目要最少的反转次数即可,见代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int len = 16;
char c[4][4];
/*
wwww
wwww
wwww
wwww
*/
//二进制下标
/*
15 14 13 12
11 10 9 8
7 6 5 4
3 2 1 0
*/
bool isok(int status_)
{
if(status_ == 0 || status_ == 0xffff)
return true;
else
return false;
}
void flip(int &status_, int i)//这里用状态副本的引用在此函数里的修改可以直接更改状态副本的值
{
status_ ^= 1 << i;//首先对对应位置的改变,其次对上下左右相邻的位置进行改变
if(i < 12){//up
status_ ^= 1 << (i+4);
}
if(i > 3){//down
status_ ^= 1 << (i-4);
}
if((i%4+1) != 4){//left
status_ ^= 1 << (i+1);
}
if((i%4) != 0){//right
status_ ^= 1 << (i-1);
}
}
int main()
{
while(scanf("%s", c) != EOF){
bool ok = false;
int cnt = 20;
int num = len;
int status = 0;
for(int i = 1; i <= 3; i++){
getchar();
scanf("%s", c[i]);
}
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
--num;
if(c[i][j] == 'w'){ //w 为1,b为0
status ^= 1 << num;//status代表当前的初始状态
}
}
}
for(int i = 0; i <=0xffff; i++){
int status_ = status;//代表初始状态的副本
int step = 0;
for(int j = 0; j <= 15; j++){
if((1<<j)&i){
flip(status_, j);//按照不同的所有的反转样式的反转方法反转回去看有没有符合题目要求的
step++;
}
if(isok(status_) && step <= 16){
ok = true;
cnt = min(cnt, step);
}
}
}
if(ok){
printf("%d\n", cnt);
}else{
printf("Impossible\n");
}
}
return 0;
}