题目链接 http://poj.org/problem?id=1681
题意:有一个n*n的正方形,每个区域开始都是白色或者黄色的,现在想把整个区域都刷成黄色,每次都可以选择一个区域,然后这个区域以及和它相邻的四个区域都会同时改变颜色,问最后能不能把所有的区域都刷成黄色,如果可以,输出需要的最少的粉刷次数,如果不能就输出inf。
思路:高斯消元,每个区域的最终的颜色都只和自己的初始颜色,自己和周围的四块区域的一共粉刷次数有关,所以我们可以把每个区域都列出一个方程,然后再用高斯消元来求解整个方程组有没有解,根据题意,我们可以知道,每个区域最多只会刷一次,因为刷两次那么就和没刷一样,所以每个未知数的解就只有0或者1,只要解出来,那么就是最少的粉刷数。其实只要把矩阵写出来,剩下的就是一道模板题了。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int mat[250][250];
int n;
int an[250];
char s[250][250];
int x[5]={0,0,0,1,-1};
int y[5]={0,1,-1,0,0};
bool ok(int a,int b){
if(a<0||a>=n||b<0||b>=n)
return false;
return true;
}
int Gauss(){
int i,j;
for(i=0,j=0;i<n*n&&j<n*n;i++,j++){
int k=i;
for(int p=i+1;p<n*n;p++)
if(abs(mat[k][j])<abs(mat[p][j])) k=p;
if(k!=i){
for(int p=j;p<=n*n;p++)
swap(mat[i][p],mat[k][p]);
}
if(mat[i][j]==0){
i--;
continue;
}
for(int p=i+1;p<n*n;p++) if(mat[p][j]){
int lcm=abs(mat[i][j])/__gcd(abs(mat[i][j]),abs(mat[p][j]))*abs(mat[p][j]);
//cout<<lcm<<' '<<mat[i][j]<<' '<<mat[p][j]<<endl;
int ta=lcm/abs(mat[p][j]),tb=lcm/abs(mat[i][j]);
if(mat[i][j]*mat[p][j]<0) tb=-tb;
for(int g=j;g<=n*n;g++)
mat[p][g]=(mat[p][g]*ta-mat[i][g]*tb)%2;
}
}
for(int p=i;p<n*n;p++)
if(mat[p][n*n]) return -1;
for(int p=n*n-1;p>=0;p--){
int tem=mat[p][n*n];
for(int g=p+1;g<n*n;g++)
tem-=mat[p][g]*an[g];
an[p]=tem;
an[p]=(an[p]%2+2)%2;
}
int ans=0;
for(int p=0;p<n*n;p++)
if(an[p]) ans++;
return ans;
}
int main()
{
int cas;
cin>>cas;
while(cas--){
memset(mat,0,sizeof(mat));
memset(an,0,sizeof(an));
cin>>n;
for(int i=0;i<n;i++){
scanf("%s",s[i]);
for(int j=0;j<n;j++) if(s[i][j]=='w')
mat[i*n+j][n*n]=1;
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
for(int k=0;k<5;k++){
int xx=i+x[k];
int yy=j+y[k];
if(ok(xx,yy)) mat[i*n+j][xx*n+yy]=1;
}
}
}
int num=Gauss();
if(num==-1)
cout<<"inf"<<endl;
else cout<<num<<endl;
}
return 0;
}