4171: Rhl的游戏
Description
RHL最近迷上一个小游戏:Flip it。游戏的规则很简单,在一个N*M的格子上,有一些格子是黑色,有一些是白色
每选择一个格子按一次,格子以及周围边相邻的格子都会翻转颜色(边相邻指至少与该格子有一条公共边的格子
),黑变白,白变黑。RHL希望把所有格子都变成白色的。不幸的是,有一些格子坏掉了,无法被按下。这时,它
可以完成游戏吗?
Input
第一行一个整数T,表示T组数据。
每组数据开始于三个整数n,m,k,分别表示格子的高度和宽度、坏掉格子的个数。接下来的n行,每行一个长度m的
字符串,表示格子状态为‘B‘或‘W‘。最后k行,每行两个整数Xi,Yi(1≤Xi≤n,1≤Yi≤m),表示坏掉的格子。
n,m,k<=256,T<=10
Output
对于每组数据,先输出一行Case #i: (1≤i≤T)
如果可以成功,输出YES,否则输出NO。
Sample Input
2
3 3 0
WBW
BBB
WBW
3 3 2
WBW
BBB
WBW
2 2
3 2
Sample Output
Case #1:
YES
Case #2:
NO
如果n,m的范围比较小,那么直接令 Xi,j X i , j 表示 (i,j) ( i , j ) 格子按不按,然后直接高斯消元,不能按的限制等价于一个方程 Xi,j=0 X i , j = 0
当n,m比较大的时候,直接高斯消元虽然可以利用他比较稀疏的特点优化时间,但空间还是比较卡。
这时考虑缩小变量规模,可以利用
X1,i
X
1
,
i
表示出
X2,i
X
2
,
i
进而将
Xn,i
X
n
,
i
表示出来,这样变量规模就缩小到了n的规模。
这依赖于一个等式,对于点 (i,j) ( i , j ) ,有
那么利用这个等式,就可以在已知第 i i 行和行的线性表示的情况下,表示出 i+1 i + 1 行关于 Xi,1...m X i , 1... m 的线性表示。
举个例子就是 X2,3=X1,3⊕X1,2⊕X1,4⊕map[1][3] X 2 , 3 = X 1 , 3 ⊕ X 1 , 2 ⊕ X 1 , 4 ⊕ m a p [ 1 ] [ 3 ] ,这里的 map[1][3] m a p [ 1 ] [ 3 ] 表示 (1,3) ( 1 , 3 ) 这个格子是 0 or 1 0 o r 1
那么以此类推到最后一行后,就可以利用 (n,j) ( n , j ) 点的等式来列出一个关于 X1,1...m X 1 , 1... m 的方程,总共有m个方程,m个未知数。
对于不能按的限制,由于已知了每个点的线性表示,那么也可以变成一个方程。最后高斯消元即可。
代码实现的时候只需要用一个 bitset b i t s e t , F[i][j] F [ i ] [ j ] 表示 (i,j) ( i , j ) 的线性表示中 X1,1...m X 1 , 1... m 的系数,另外用一个 int i n t 数组记录一下 map[i][j] m a p [ i ] [ j ] 的异或和即可。
总复杂度 O(Tn332) O ( T n 3 32 )
代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<bitset>
#define N 260
using namespace std;
char s[N][N];
bitset<260>map[N],F[N][N],G[N],A,B,C[N];
int T,n,m,k;
bool Gauss(int row,int col)
{
int i,j,k,MR,x,y;
for(x=1,y=1,MR=1;x<=row&&y<col;x++,y++,MR=x)
{
for(i=x;i<=row;i++)if(C[i][y]==1)MR=i;
if(C[MR][y]==0){x--;continue;}
swap(C[x],C[MR]);
for(i=x+1;i<=row;i++)if(C[i][y])C[i]^=C[x];
}
for(i=x;i<=row;i++)if(C[i][col]==1)return 0;
return 1;
}
int main()
{
int i,j,tot,x,y,t;
scanf("%d",&T);
for(t=1;t<=T;t++)
{
scanf("%d%d%d",&n,&m,&k);
memset(C,0,sizeof(C));
memset(map,0,sizeof(map));
memset(G,0,sizeof(G));
memset(F,0,sizeof(F));
for(i=1;i<=n;i++)scanf("\n%s",&s[i][1]);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)if(s[i][j]=='B')map[i][j]=1;
for(i=1;i<=m;i++)F[1][i][i]=1;
for(i=2;i<=n;i++)
for(j=1;j<=m;j++)
{
F[i][j]=F[i-1][j-1]^F[i-1][j]^F[i-1][j+1]^F[i-2][j];
G[i][j]=G[i-1][j-1]^G[i-1][j]^G[i-1][j+1]^G[i-2][j]^map[i-1][j];
}
for(i=1;i<=m;i++)
{
C[i]=F[n][i]^F[n][i-1]^F[n][i+1]^F[n-1][i];
C[i][m+1]=G[n][i]^G[n][i-1]^G[n][i+1]^G[n-1][i]^map[n][i];
}
tot=m;
while(k--)
{
scanf("%d%d",&x,&y);
C[++tot]=F[x][y];
C[tot][m+1]=G[x][y];
}
if(Gauss(tot,m+1))printf("Case #%d:\nYES\n",t);
else printf("Case #%d:\nNO\n",t);
}
}