【题目链接】 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; }