[关键字]:数学 异或方程组
[题目大意]:有n各工人n各水管,每个工人可以对某几个水管进行反操作(开变关关变开),问需要操作那些人才会大开所有水管。
//==================================================================================
[分析]:看懂题目后不难可以列出下面这个方程组:
x1*a[1,1] xor x2*a[1,2] xor x3*a[1,3] xor …… xor xn*a[1,n]=1
x1*a[2,1] xor x2*a[2,2] xor x3*a[2,3] xor …… xor xn*a[2,n]=1
x1*a[3,1] xor x2*a[3,2] xor x3*a[3,3] xor …… xor xn*a[3,n]=1
……
x1*a[n,1] xor x2*a[n,2] xor x3*a[n,3] xor …… xor xn*a[n,n]=1
其中xi表示第i个工人是否需要(要xi=1;不要xi=2),a[i,j]表示第j个工人是否能对第i个水阀进行操作。
然后就是怎么解这个异或方程组了,也是用高斯消元,只不过在加法的高斯消元中是通过先除再乘把系数化成一样的再相减,因为减是加的逆运算。而异或地逆运算还是异或,所以要先把系数化相同在xor掉同项。
[代码]:
View Code
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int n,ans; int a[260][260]; void Init() { memset(a,0,sizeof(a)); scanf("%d",&n); for (int i=1;i<=n;++i) { int x; while (scanf("%d",&x),x!=-1) a[x][i]=1; a[i][n+1]=1; } } void Solve() { for (int i=1;i<=n;++i) { int t=i; while (!a[t][i]) ++t; if (!a[t][i]) {ans=-1;return;}; for (int j=i;j<=n+1;++j) swap(a[i][j],a[t][j]); for (int j=i+1;j<=n;++j) if (a[j][i]!=0) { for (int k=i;k<=n+1;++k) a[j][k]^=a[i][k]; } } ans=a[n][n+1]; for (int i=n-1;i>=1;--i) { for (int j=i+1;j<=n;++j) a[i][n+1]^=a[i][j]*a[j][n+1]; ans+=a[i][n+1]; } for (int i=1;i<=n;++i) if (a[i][n+1]) { printf("%d",i),--ans; if (!ans) printf("\n"); else printf(" "); } } int main() { Init(); Solve(); if (ans==-1) printf("No solution\n"); return 0; }