题目大意:翻动一枚棋子,周围四枚棋子也要翻动,问最少翻动几次可以让棋盘上所有棋子都是黑色或者都是白色。
解题报告:这道题做法很多,可以用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");
}
}