题目大意::给出起始棋局,每次按一个地方,上下左右如果有棋子都要翻转,问全部翻成同种颜色最少的步骤。
思路:枚举、位运算
1.将求全部翻成同种颜色最少的步骤分解为两个子问题,先求全部翻成白色的最少步骤(顺序可以倒置),再求全部翻成蓝色的最少步骤,然后求最小值,第一个子问题中,可以把白色看成0,蓝色看成1,求把1全部变为0的最少步骤,那么开两个数组分别存两个子问题的数据。
2.用一个整数的二进制存每一列的状态、以及对每一列的操作,因为一列只有四行,所以这个数的范围是,15的二进制数是1111。
3.每次确定第一列取什么操作,然后从第2列到第4列,每一列的操作取决于前一列什么位置有1(1表示翻转),那么对应的行也翻转。
4.因为第4列是最后一列,如果第4列全部是0(0表示是需要翻成的颜色),那么就可以跟新答案,否则重新枚举。
Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=6;
int num[16]={0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
int a[maxn],b[maxn],x[maxn],c[maxn];
int ans = INT_MAX;
void work(int a[]) {
    for(x[1]=0;x[1]<=15;++x[1]) {
        int cnt=num[x[1]];
        c[1]=a[1]^x[1]^(x[1]>>1)^( (x[1]<<1)&15 );//第一排执行完x[1]后,第一列的状态
        c[2]=a[2]^x[1];//第一排执行完x[1]后,第二列的状态
        for(int i=2;i<=4;++i) {
            x[i]=c[i - 1];//第i-1列某一行为1时,就在第i列的对应行取1 
            cnt+=num[x[i]];
            c[i]=c[i]^x[i]^(x[i]>>1)^( (x[i]<<1)&0xf );
            c[i+1]=a[i+1]^x[i];
        }//处理第i列,使得第i-1列全为0
         if (!c[4])
            ans = min(ans, cnt);
    }
}
int main() {
    for (int i = 1; i <= 4; ++i)
    for (int j = 0; j < 4; ++j) {
        char s;
        scanf(" %c", &s);
        if (s == 'b')   a[i] |= 1 << j;
        else b[i] |= 1 << j;
    }
    work(a);
    memset(c,0,sizeof(c));
    work(b);
    if (ans==INT_MAX) puts("Impossible");
    else  printf("%d\n", ans);
    return 0;
}