高斯消元模板(有多个解。无解输出“零”,有解输出“2^自由元的个数”)
int luf[SIZE_A][SIZE_A],B[SIZE_A][SIZE_A];
int n,m;
long long Gauss()
{
int cnt = 0;
for (int i = 0; i < m; i++){//每个开关代表的就是未知数,i代表每个多项式前面的系数
int j;
for (j = cnt; j < n; j++)//找第一个有xi的那一行,第一个xi不为0的位置
if (B[j][i]){//如果x1刚好换到了第一行没有的位置怎么办啊,那么就不作为!
swap(B[j],B[cnt]);//把当前行换到cnt那一行
break;
}
if (j <n){// 说明一定存在xi
for (j = cnt+1; j < n; j++)//第j行
if (B[j][i])
for (int k = 0; k <= m; k++)//每一行都减去对应倍的第一行。
B[j][k] ^= B[cnt][k];//把cnt下面的每一行都和cnt异或
cnt++;
}
}
for (int i = 0; i < n; i++){
if (B[i][m] != 0){//按位或运算更快一些
int t = 0;
for (int j = 0; j < m; j++)
t |= B[i][j];
if (t == 0)
return 0LL;
}
}//还要判断有没有解!!!!!!!
return 1LL<<(m-cnt);//开关的个数才是未知数的个数啊!
}
题意:有n个灯,m个开关。每个开关控制对应的灯,问有多少种方案数可以达到想要的开灯效果,初始状态全是关灯。
题解:既然我都知道了是高斯消元……
A开关:1,2
B开关:1,3
三盏灯状态:0 1 1
第一个方程: x ^ y = 0
第二个方程: x = 1
第三个方程: y = 1
A开关: 1,3
B开关: 2,3,4
C开关: 1,2,4
D开关: 1,3,4
四盏灯状态 ,0111
第一个方程: x1^x3^x4 = 0
第二个方程:x2^x3 = 1
第三个方程:x1^x2^x4 = 1
第四个方程:x2^x3^x4 = 1
把第一个有xi出现的式子换到第i行
然后每一行和这一行异或。
为什么呢?
加法的高斯消元的宗旨是:“消去"某个未知数
比如2x+y=9 x+4y = 10
1式减去两倍的2式就是为了消去未知数x。
而在异或运算中,如果想要消去某个未知数,那就异或上它的本身就好了
比如:消去x1后原方程组变成
x1^x3^x4 = 0
x2^x3 = 1
x2^x3 = 1
x2^x3^x4 = 1
消去x2变成了
x1^x3^x3 = 0
x2^x3 = 1
0
x4 = 1
消去x4之后 原式不变
end
然后算可以知道解的有a个未知数,不知道解的有b个未知数,
也就是说,这b个自由元啊,可以随便开开关关可以是0可以是1,所以解的个数就是2^b。
代码:
#include<bits/stdc++.h>
#define SIZE_A 330
using namespace std;
int luf[SIZE_A][SIZE_A],B[SIZE_A][SIZE_A];
int n,m;
long long Gauss()
{
int cnt = 0;
for (int i = 0; i < m; i++){//每个开关代表的就是未知数,i代表每个多项式前面的系数
int j;
for (j = cnt; j < n; j++)//找第一个有xi的那一行,第一个xi不为0的位置
if (B[j][i]){//如果x1刚好换到了第一行没有的位置怎么办啊,那么就不作为!
swap(B[j],B[cnt]);//把当前行换到cnt那一行
break;
}
if (j <n){// 说明一定存在xi
for (j = cnt+1; j < n; j++)//第j行
if (B[j][i])
for (int k = 0; k <= m; k++)//每一行都减去对应倍的第一行。
B[j][k] ^= B[cnt][k];//把cnt下面的每一行都和cnt异或
cnt++;
}
}
for (int i = 0; i < n; i++){
if (B[i][m] != 0){//按位或运算更快一些
int t = 0;
for (int j = 0; j < m; j++)
t |= B[i][j];
if (t == 0)
return 0LL;
}
}//还要判断有没有解!!!!!!!
return 1LL<<(m-cnt);//开关的个数才是未知数的个数啊!
}
int main()
{
//freopen("input.txt","r",stdin);
int T;
while (~scanf("%d",&T)){
for (int cas = 1; cas <= T; cas++){
scanf("%d %d",&n,&m);//n盏灯,m个开关
memset(luf,0,sizeof(luf));//全部关上
int k;
for (int i = 0; i < m; i++){
scanf("%d",&k);
while (k--){
int temp;
scanf("%d",&temp);//temp代表控制第temp-1那个灯
luf[temp-1][i] = 1;//m个开关里面的第i个控制了第temp-1那个灯
}
}
int Q;
scanf("%d",&Q);
printf("Case %d:\n",cas);
while (Q--){
for (int i = 0; i < n; i++){
for (int j = 0; j <= m; j++)
B[i][j] = luf[i][j];
}//每进行一次高斯消元,都要把自己的位置改变一次!!注意不要改变原数组
for (int i = 0; i < n; i++){
int temp;
scanf("%d",&temp);
B[i][m] = temp;
}
printf("%I64d\n",Gauss());
}
}
}
return 0;
}
这道题是异或之后有多解的方案数,那一个解的在么么哒书上有。
书上说还可以处理方程数和未知数的数量不相等或者解不唯一的情况。还可以求得行列式的值和矩阵的秩(我觉得求秩就是方案数)。