POJ 2345 Central heating(高斯消元)

【题目链接】 http://poj.org/problem?id=2345

【题目大意】

  给出n个开关和n个人,每个人可以控制一些开关,现在所有的开关都是关着的
  一个指令可以让一个人掰动所有属于他控制的开关,使得开关的状态变化,
  现在要求求出最少的指令,使得开关全开,按字典序输出指令的人

【题解】

  我们将人的操作作为变元建立方程组,因为一个人相同的两次操作对结果是没有影响的
  我们可以认为这是无效操作,所以每个人操作的次数只是一次或者零次,
  我们解01方程组即可得到答案。

【代码】

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
//对2取模的01方程组
const int MAXN=300;
//有equ个方程,var个变元。增广矩阵行数为equ,列数为var+1,分别为0到var
int equ,var;
int a[MAXN][MAXN]; //增广矩阵
int x[MAXN]; //解集
int free_x[MAXN];//用来存储*变元(多解枚举*变元可以使用)
int free_num;//*变元的个数
//返回值为-1表示无解,为0是唯一解,否则返回*变元个数
int Gauss(){
int max_r,col,k;
free_num = 0;
for(k=0,col=0;k<equ&&col<var;k++,col++){
max_r=k;
for(int i=k+1;i<equ;i++){
if(abs(a[i][col])>abs(a[max_r][col]))max_r=i;
}
if(a[max_r][col] == 0){
k--;
free_x[free_num++]=col;//这个是*变元
continue;
}
if(max_r != k){
for(int j=col;j<var+1;j++)swap(a[k][j],a[max_r][j]);
}
for(int i=k+1;i<equ;i++){
if(a[i][col]!=0){
for(int j=col;j<var+1;j++)a[i][j]^=a[k][j];
}
}
}
for(int i=k;i<equ;i++)if(a[i][col]!=0)return -1;//无解
if(k<var)return var-k;//*变元个数
//唯一解,回代
for(int i=var-1;i>=0;i--){
x[i]=a[i][var];
for(int j=i+1;j<var;j++)x[i]^=(a[i][j]&&x[j]);
}return 0;
}
int n;
int main(){
while(~scanf("%d",&n)){
memset(a,0,sizeof(a));
for(int i=0;i<n;i++){
int t;
while(scanf("%d",&t),t!=-1){
a[t-1][i]=1;
}
}for(int i=0;i<n;i++)a[i][n]=1;
equ=n,var=n;
Gauss();
int flag=0;
for(int i=0;i<n;i++){
if(x[i]){
if(flag++)printf(" %d",i+1);
else printf("%d",i+1);
}
}puts("");
}return 0;
}
上一篇:VMS项目总结


下一篇:java语言中Object对象的hashCode()取值的底层算法是怎样实现的