题目大意::给出起始棋局,每次按一个地方,上下左右如果有棋子都要翻转,问全部翻成同种颜色最少的步骤。
思路:枚举、位运算
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; }