题目大意:翻动一枚棋子,周围四枚棋子也要翻动,问最少翻动几次可以让棋盘上所有棋子都是黑色或者都是白色。
解题报告:这道题做法很多,可以用BFS,暴搜+位运算。这些写起来都不难,而且都可以A掉。不过最近在学习高斯消元法,所以仍然用高斯消元法去做。如果题目变成8*8,高斯消元依然可以做,暴搜就不一定了= =。
这题用高斯消元的难度在于矩阵是存在4个变元的,而且直接求解的话不能求得任何一个变元。在下也不会,不过搜一搜就会了。
在利用初等行变换来把增广矩阵转换成行阶梯阵时,如果col列中的元素全为0,那么此时的col列就是变元。此时记录col列的位置。
我们可以在化简完矩阵后对变元进行枚举。该题中变元的值不是0就是1,可以直接使用位运算确定每个变元的值。然后根据已经枚举的值去确定剩下的非变元,并寻找优化的解。注意全1或者全0都是解,所以求解两次。
代码如下:
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; typedef int Matrix[16][17]; int Gauss(Matrix a, int equ, int var) { bool free[17]; memset(free, 0, sizeof(free)); int freePos[17]; int freeNum=0; int x[17]; int row=0; int col=0; while(row<equ && col<var) { int r=row; for(int i=row+1;i<equ;i++) if(a[i][col]) r=i; if(a[r][col]==0) { free[col]=true; freePos[freeNum++]=col; col++; continue; } for(int j=col;j<=var;j++) swap(a[row][j], a[r][j]); for(int i=row+1;i<equ;i++) if(a[i][col]) for(int j=col;j<=var;j++) a[i][j]^=a[row][j]; row++; col++; } for(int i=row;i<equ;i++) if(a[i][col]) return 20; int res=20; for(int s=0;s<(1<<freeNum);s++) { int cnt=0; int binary=s; memset(x, 0, sizeof(x)); for(int j=0;j<freeNum;j++, binary>>=1) if(binary&1) { x[freePos[j]]=1; cnt++; } for(int i=row-1;i>=0;i--) if(!free[i]) { int tmp=a[i][var]; for(int j=i+1;j<var;j++) if(a[i][j]) tmp^=x[j]; x[i]=tmp; if(tmp) cnt++; } res=min(res, cnt); } return res; } int main() { int b[16]; for(int i=0;i<16;) { char ch=getchar(); if(ch=='\n') continue; b[i]=(ch=='b'?1:0); i++; } Matrix a; memset(a, 0, sizeof(a)); for(int i=0;i<16;i++) { a[i][16]=b[i]; a[i][i]=1; if(i<12) a[i][i+4]=1; if(i>=4) a[i][i-4]=1; if(i%4!=0) a[i][i-1]=1; if(i%4!=3) a[i][i+1]=1; } int Min=Gauss(a, 16, 16); memset(a,0,sizeof(a)); for(int i=0;i<16;i++) { a[i][16]=1-b[i]; a[i][i]=1; if(i<12) a[i][i+4]=1; if(i>=4) a[i][i-4]=1; if(i%4!=0) a[i][i-1]=1; if(i%4!=3) a[i][i+1]=1; } Min=min(Min,Gauss(a, 16, 16)); if(Min==20) puts("Impossible"); else printf("%d\n",Min); }
附BFS代码:
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; typedef unsigned short Status; bool vis[1<<16]; Status step[1<<16]; Status stack[1<<16]; Status change[16]= { 0x13, 0x27, 0x4E, 0x8C, 0x131, 0x272, 0x4E4, 0x8C8, 0x1310, 0x2720, 0x4E40, 0x8C80, 0x3100, 0x7200, 0xE400, 0xC800 }; int bfs(Status n) { memset(vis, 0, sizeof(vis)); memset(step, 0, sizeof(step)); int top=0, bot=0; stack[top++]=n; step[n]=0; vis[n]=true; while(bot<top) { Status now=stack[bot++]; for(int i=0;i<16;i++) if(!vis[change[i]^now]) { stack[top++]=change[i]^now; step[change[i]^now]=step[now]+1; vis[change[i]^now]=true; } if(vis[0]) return step[0]; if(vis[(1<<16)-1]) return step[(1<<16)-1]; } return -1; } int main() { char ch[4][5]; while(~scanf("%s%s%s%s", ch[0], ch[1], ch[2], ch[3])) { Status n=0; for(int i=0;i<4;i++) for(int j=0;j<4;j++) if(ch[i][j]=='b') n|=1<<(i*4+j); int ans = bfs(n); if(~ans) printf("%d\n", ans); else puts("Impossible"); } }