poj 1681 Painter's Problem

/*
题意:类似于poj 1222 EXTENDED LIGHTS OUT ,只不过换成了格子是黄色(0)还是白色(1),
要求输出最小次数,并且不保证有唯一解。所以如果方程组
(1)无解,输出"inf" (2)有唯一解,当然输出唯 一解;
(3)无穷解:需要枚举自由元的状态,即如果有两个自由元我们需要枚举 (1<<2) 个状态,再比较得出最小的次数
但本题数据较水,不需枚举,每次将自由变量置为0一样可以过.
而且因为x[]初始化为0,所以直接对x[i]=1进行计数就好了

*/



#include <iostream> //高斯消元
#include <cstring>

using namespace std;
#define MAXN 226
int equ , var ,n ; //equ指方程个数,var指未知数的个数,即矩阵的行和列
int a[MAXN][MAXN], x[MAXN]; //a表示增广矩阵和x表示方程组的解,而a的最后一列表示常数项
int gcd(int a,int b)

{
if (a==0||b==0)
return max(a,b);
int i=0,j=0;
while (a&1==0)
{
++i;
a=a>>1;
}
while (b&1==0)
{
++j;
b=b>>1;
}
i=min(i,j);
while(1)
{
if(a<b)
swap(a,b);
a-=b;
if(a==0)
return b<<i;
while(a&1==0)
{
a=a>>1;
}
}
}
int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
void init() //初始化增广矩阵和方程组的解
{

scanf("%d",&n);
equ = var = n*n; // 矩阵的行列数都为 n*n
memset(a,0,sizeof(a));

memset(x,0,sizeof(x));
char ch[20];
for(int i = 0;i < n; ++i)
{
scanf("%s",ch);
for(int j = 0;j < n; ++j)
{
if(ch[j] == 'w') //白色为1,黄色为0,目标状态是全为黄色
a[i*n+j][n*n] = 1;

else
a[i*n+j][n*n] = 0;
}
}
int move[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
for(int i = 0;i < n; ++i) // 系数矩阵a[i][j]为1当且仅当按下第j个灯会切换第i个灯的状态
{

for(int j = 0;j < n; ++j)
{
a[i*n+j][i*n+j] = 1 ;
for(int k = 0;k < 4; ++k)
{
int tx = i + move[k][0];
int ty = j + move[k][1];
if(!(tx >= 0 && tx < n && ty >= 0 && ty < n))
continue;
a[tx*n+ty][i*n+j] = 1 ; //按下灯[i][j]会影响到灯[tx][ty]
}

}
}
}
int gauss()
{
int k,col = 0; //当前处理的列
for(k = 0;k < equ && col < var;++k,++col)

{
int max_r = k;
for(int i = k+1;i < equ; ++i)
if(a[i][col] > a[max_r][col])
max_r = i;
if(max_r != k)
{
for(int i = k;i < var + 1; ++i)
swap(a[k][i],a[max_r][i]);
}
if(a[k][col] == 0)
{
k--;
continue;
}
for(int i = k+1;i < equ; ++i)
{
if(a[i][col] != 0)
{
int LCM = lcm(a[i][col],a[k][col]);
int ta = LCM/a[i][col], tb = LCM/a[k][col];
if(a[i][col]*a[k][col] < 0)
tb = -tb;
for(int j = col;j < var + 1; ++j)
a[i][j] = ( (a[i][j]*ta)%2 - (a[k][j]*tb)%2 + 2 ) % 2; //a[i][j]只有0和1两种状态
}

}
}
for(int i = k;i < equ; ++i)
if(a[i][col] != 0) return -1; // 无解返回 -1

//唯一解或者无穷解,k<=var
for(int i = k-1;i >= 0; --i)

{
int tmp = a[i][var] % 2;
for(int j = i+1;j < var; ++j)
if(a[i][j] != 0)
tmp = ( tmp - (a[i][j]*x[j])%2 + 2 ) % 2;
x[i] = (tmp/a[i][i]) % 2;
}
return 0;
}
int main()
{
int cases;
scanf("%d",&cases);
for(int k=1;k<=cases;++k)
{
init();
int ans = gauss();
if(ans == -1)
printf("inf\n");
else
{
int cnt = 0;
for(int i = 0;i < n*n; ++i)
if(x[i]==1) //表示按下该按钮
cnt++;

printf("%d\n",cnt);
}
}
return 0;
}

posted on 2012-03-17 14:29  sysu_mjc  阅读(268)  评论(0编辑  收藏  举报

导航