设其次方程组:
a11*x1+a12*x2+a13*x3...+a1n*xn=b1,
a21*x1+a22*x2+a23*x3...+a2n*xn=b2,
.....
am1*x1+am2*x2+am3*x3...+amn*xn=b3.
写成矩阵形式:
系数矩阵:
a11 a12 ... a1n
a21 a22 ... a2n
. . .
am1 am2 ... amn
解的矩阵
b1
b2
.
bn
增广矩阵
a11 a12 ... a1n b1
a21 a22 ... a2n b2
. . . .
am1 am2 ... amn bn
如果系数矩阵和增广矩阵的秩相等,有解(秩等于变元个数有唯一解,否则有多解)。秩不相等无解。
对于灯泡开关问题,系数矩阵相当于操作矩阵,N个灯泡就构成N*(N+1)的矩阵,矩阵只由0,1组成,矩阵的关系不是相加而是异或!把一开始的状态赋给b1,b2...bn(a[i][N],下标从0开始),第一列代表第一个灯泡,把它和它相关的灯泡都设成1。比如第一个灯泡和第二、三个灯泡相关,就把a[1][1],a[2][1],a[3][1]设成1。把每个灯泡都这么做。构造完矩阵后求解就行了。
这样构造矩阵的原因是,可以由灯泡的开关次数xi列出线性方程组,矩阵a元素值最大是1,因为重复开关没意义,xi的取值会影响和灯泡i相关的灯泡的取值。
a11*x1^a12*x2^a13*x3...^a1n*xn=b1,
a21*x1^a22*x2^a23*x3...^a2n*xn=b2,
.....
am1*x1^am2*x2^am3*x3...^amn*xn=b3.
求解的时候可能0解,1解,多解,多解的时候可能会要求和最小的那个。有了模版每次只要构造矩阵就行了~
![得意](http://static.blog.csdn.net/xheditor/xheditor_emot/default/proud.gif)
int equ,var;//equ个方程,var个变元
int a[MAXN][MAXN];//增广矩阵,行数为equ,列数为var+1,从0开始
int x[MAXN];//解集
int free_x[MAXN];//储存自由变元
int free_num;//自由变元个数
//针对灯泡问题的异或高斯消元
int xor_gauss(){
int row=0,col=0;//当前矩阵阶梯下降了row次,当前在处理第col列
free_num=0;
for(;row<equ&&col<var;col++){
int r;
for(r=row;r<equ;r++) if(a[r][col]) break;//找当前row或者往下第一个a[r][col]!=0,r>=row
//没找到,说明这一列矩阵阶梯没有下降,x[col]是自由变元,可以取任意值
if(!a[r][col]){
free_x[free_num++]=col;
continue;
}
if(row!=r) for(int i=0;i<var+1;i++) swap(a[row][i],a[r][i]);//找到了,并且row!=r,把第row行和第r行交换
//把row下面的行a[i][j]!=0的变为0,即和第row行相减
for(int i=row+1;i<equ;i++) if(a[i][col]){
for(int j=col;j<var+1;j++) a[i][j]^=a[row][j];
}
row++;//阶梯下降
}
for(int i=row;i<equ;i++) if(a[i][var]) return -1;//增广矩阵的秩如果大于方程组举证的秩,无解
if(row<var) return var-row;//自由变元个数
//唯一解,回带
for(int i=var-1;i>=0;i--){
x[i]=a[i][var];
for(int j=i+1;j<var;j++) if(a[i][j]) x[i]^=x[j];//x[i]之后的解都已经确定,由此推出x[i]
}
return 0;
}
//枚举自由变元,以返回所有解的和最小为例
int enum_free(int n){//n个自由变元,n>0
//状态压缩枚举
int ret=INF,S=(1<<n);
for(int S0=0;S0<S;S0++){
int cnt=0;//统计所有解的和
for(int j=0;j<n;j++){
if((1<<j)&S0){
x[free_x[j]]=1;
cnt++;
}
else x[free_x[j]]=0;
}
//还剩var-n个变元
for(int i=var-n-1;i>=0;i--){
int idx;//当前要找非自由变元x[idx]
for(idx=i;idx<var;idx++) if(a[i][idx]) break;//找到第i行第一个非0的元素
x[idx]=a[i][var];
for(int j=idx+1;j<var;j++) if(a[i][j]) x[idx]^=x[j];
cnt+=x[idx];
}
ret=min(ret,cnt);
}
return ret;
}