高斯消元求解异或方程组:
比较不错的一篇文章:http://blog.sina.com.cn/s/blog_51cea4040100g7hl.html
cojs.tk 539. 牛棚的灯
★★☆ 输入文件:lights.in
输出文件:lights.out
简单对比
时间限制:1 s 内存限制:128 MB
【问题描述】
贝希和她的闺密们在她们的牛棚中玩游戏。但是天不从人愿,突然,牛棚的电源跳闸了,所有的灯都被关闭了。贝希是一个很胆小的女生,在伸手不见拇指的无尽的黑暗中,她感到惊恐,痛苦与绝望。她希望您能够帮帮她,把所有的灯都给重新开起来!她才能继续快乐地跟她的闺密们继续玩游戏!
牛棚中一共有N(1 <= N <= 35)盏灯,编号为1到N。这些灯被置于一个非常复杂的网络之中。有M(1 <= M <= 595)条很神奇的无向边,每条边连接两盏灯。
每盏灯上面都带有一个开关。当按下某一盏灯的开关的时候,这盏灯本身,还有所有有边连向这盏灯的灯的状态都会被改变。状态改变指的是:当一盏灯是开着的时候,这盏灯被关掉;当一盏灯是关着的时候,这盏灯被打开。
问最少要按下多少个开关,才能把所有的灯都给重新打开。
数据保证至少有一种按开关的方案,使得所有的灯都被重新打开。
题目名称:lights
输入格式:
*第一行:两个空格隔开的整数:N和M。
*第二到第M+1行:每一行有两个由空格隔开的整数,表示两盏灯被一条无向边连接在一起。
没有一条边会出现两次。
样例输入(文件 lights.in):
5 6
1 2
1 3
4 2
3 4
2 5
5 3
输入细节:
一共有五盏灯。灯1、灯4和灯5都连接着灯2和灯3。
输出格式:
第一行:一个单独的整数,表示要把所有的灯都打开时,最少需要按下的开关的数目。
样例输出(文件 lights.out):
3
输出细节:
按下在灯1、灯4和灯5上面的开关。
hwzer的代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #define inf 1000000000 8 #define ll long long 9 using namespace std; 10 inline int read() 11 { 12 int x=0,f=1;char ch=getchar(); 13 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 14 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 15 return x*f; 16 } 17 int n,m,tot; 18 int mn=inf; 19 int f[45][45],ans[45]; 20 void gauss() 21 {/*一共有n个方程,n个变量*/ 22 for(int i=1;i<=n;i++) 23 { 24 int j=i;/*第i行第i列是要向下消元的,如果该行是0,就找一个不是0的一行和他互换后,向下消元,如果j>n,说明所有行的第i列都是0,那就不用消元了*/ 25 while(j<=n&&!f[j][i])j++; 26 if(j>n)continue; 27 if(i!=j)for(int k=1;k<=n+1;k++)swap(f[i][k],f[j][k]);/*互换*/ 28 for(int j=i+1;j<=n;j++)/*用第i行向下消元*/ 29 if(i!=j&&f[j][i])/*消元时,只消这一项不是0的方程即可*/ 30 for(int k=1;k<=n+1;k++)/*消这个方程的时候要把所有的量的都对应相消*/ 31 f[j][k]^=f[i][k]; 32 } 33 } 34 /*,因为,上面只是求出一组解,并不是最小解。 35 所以,我们需要求出所有解,然后输出最小的那个。 36 在求倒三角后,有一些m[i][i]==0,这时,我们对x[i]的取值就有两种,0或1。 37 本身,x[i]的取值对第i行的方程没有任何影响,但它的取值对其他方程有影响, 38 所以,这里需要枚举x[i]的取值。*/ 39 void dfs(int now) 40 {/*tot表示按下灯的数目*/ 41 if(tot>=mn)return;/*剪枝,取小操作,一旦大了就不用求了*/ 42 if(!now)/*搜索的终点*/ 43 { 44 mn=min(mn,tot); 45 return; 46 } 47 if(f[now][now])/*如果now不是自由变元*/ 48 {/*这就是求出当前解ans[now]的过程,利用了性质t=a^b,那么t^b等于a,最后的 f[now][n+1],是由前面的ans[i](f[now][i]不等于0)异或得出的,可以异或回去,求出ans[now] */ 49 int t=f[now][n+1]; 50 for(int i=now+1;i<=n;i++) 51 if(f[now][i])t^=ans[i]; 52 ans[now]=t; 53 if(t)tot++;/*如果当前的灯要按下,统计总数*/ 54 dfs(now-1);/*搜索上一盏灯*/ 55 if(t)tot--;/*回溯的过程,为什么可以回溯呢,因为异或方程组会有多组解,即使当前的now灯不按下,求后面的now-1仍然可以有解,说不定还可以更优,所以要回溯*/ 56 } 57 else /*如果now是自由变元,自由变元取到任何值,最终方程都会有解,就枚举x[now]是0还是1,进行搜索*/ 58 { 59 ans[now]=0;dfs(now-1); 60 ans[now]=1;tot++;dfs(now-1);tot--;/*别忘记搜索中的回溯*/ 61 } 62 } 63 int main() 64 {/*f[i][j]表示i--j有边相连,所以是1,其余的是0,在方程组中,*0后结果就没有影响了*/ 65 freopen("lights.in","r",stdin); 66 freopen("lights.out","w",stdout); 67 n=read();m=read(); 68 for(int i=1;i<=n;i++) 69 f[i][i]=1,f[i][n+1]=1; 70 for(int i=1;i<=m;i++) 71 { 72 int x=read(),y=read(); 73 f[x][y]=f[y][x]=1; 74 } 75 gauss();dfs(n);/*从n开始搜索,是因为n的变元数目少*/ 76 printf("%d\n",mn); 77 fclose(stdin);fclose(stdout); 78 return 0; 79 }
我的代码:
1 #define N 40 2 #include<iostream> 3 using namespace std; 4 #include<cstdio> 5 #include<cstring> 6 int ans[N],f[N][N],x,y,n,m; 7 int minn=(1<<31)-1,tot=0; 8 int read() 9 { 10 int sum=0,ff=1;char s; 11 s=getchar(); 12 while(s<'0'||s>'9') 13 { 14 if(s=='-') ff=-1; 15 s=getchar(); 16 } 17 while('0'<=s&&s<='9') 18 { 19 sum=sum*10+s-'0'; 20 s=getchar(); 21 } 22 return sum*ff; 23 } 24 void gauss() 25 { 26 for(int i=1;i<=n;++i) 27 { 28 int j=i; 29 while(j<=n&&!f[j][i]) j++; 30 if(j>n) continue; 31 if(i!=j) 32 { 33 for(int k=1;k<=n+1;++k) 34 { 35 swap(f[i][k],f[j][k]); 36 } 37 } 38 for(int j=i+1;j<=n;++j) 39 if(f[j][i]) 40 { 41 for(int k=1;k<=n+1;++k) 42 f[j][k]^=f[i][k]; 43 } 44 } 45 } 46 void dfs(int now) 47 { 48 if(tot>=minn) return ; 49 if(!now) 50 { 51 minn=min(minn,tot); 52 return ; 53 } 54 if(f[now][now]) 55 { 56 int t=f[now][n+1]; 57 for(int i=now+1;i<=n;++i) 58 if(f[now][i]) t^=ans[i]; 59 ans[now]=t; 60 if(t) tot++; 61 dfs(now-1); 62 if(t) tot--; 63 } 64 else 65 { 66 ans[now]=1;tot++;dfs(now-1); 67 ans[now]=0;tot--;dfs(now-1); 68 } 69 } 70 int main() 71 { 72 // freopen("lights.in","r",stdin); 73 // freopen("lights.out","w",stdout); 74 n=read();m=read(); 75 for(int i=1;i<=n;++i) 76 f[i][i]=f[i][n+1]=1; 77 for(int i=1;i<=m;++i) 78 { 79 x=read(); 80 y=read(); 81 f[x][y]=f[y][x]=1; 82 } 83 gauss(); 84 dfs(n); 85 printf("%d\n",minn); 86 // fclose(stdin); 87 // fclose(stdout); 88 return 0; 89 }