又是一个学一遍忘一遍的算法···
引入
高斯消元,是一个用来求解线性方程组的算法。
线性方程组是形如这样的方程组
实现
思路很简单,我们先把系数矩阵消成对角线形式,然后就可以得到解了。
但是会涉及到除法,所以我们要尽量减少精度误差。
解决方法是:对于每一个未知量,我们都找到系数的绝对值最大的那一项,让它去把别人消掉。
然后代码就不难得出了。
复杂度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| inline void Gauss(){ for(re int i=1,pos;i<=n;++i){ pos=0; for(re int j=i;j<=n;++j){ if(fabs(a[j][i])>fabs(a[pos][i])) pos=j; } if(fabs(a[pos][i])<eps) continue; swap(a[i],a[pos]); for(re int j=1;j<=n;++j){ if(j==i) continue; double bs=a[j][i]/a[i][i]; for(re int k=i;k<=n+1;++k) a[j][k]-=bs*a[i][k]; } } for(re int i=1;i<=n;++i){ if(fabs(a[i][i])<eps){ if(fabs(a[i][n+1])>eps){ cout<<"No Solution"; return; } else{ cout<<"Numerous Solutions"; return; } } } }
|
upd:上面这份代码有锅···
在判断无解和无数解时会出问题,这是因为我们的答案受到消元顺序影响。
详见这篇题解。
下面是一份正确的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| inline void gauss(){ for(re int i=1;i<=n;++i){ int id=i; for(re int j=1;j<=n;++j){ if(fabs(a[j][j])>eps&&j<i) continue; if(fabs(a[j][i])>fabs(a[id][i])) id=j; } if(fabs(a[id][i])<=eps) continue; swap(a[i],a[id]); for(re int j=1;j<=n;++j){ if(i==j) continue; double delta=a[j][i]/a[i][i]; for(re int k=i;k<=n+1;++k) a[j][k]-=delta*a[i][k]; } } int flag=1; for(re int i=1;i<=n;++i){ if(fabs(a[i][i])<=eps){ if(fabs(a[i][n+1])>eps) flag=-1; else if(~flag) flag=0; } } if(flag!=1){ if(flag==-1) cout<<"No Solution"; else cout<<"Numerous Solutions"; return; } for(int i=1;i<=n;i++) a[i][n+1]/=a[i][i]; }
|
拓展
异或方程组
因为异或满足交换律和结合律,所以可以像普通高斯消元那样进行消元。
只不过把加减消元换成了异或消元。
因为涉及位运算,所以可以 bitset 优化。
复杂度
1 2 3 4 5 6 7 8 9 10 11
| bitset<maxn> a[maxn]; inline void Gauss(){ for(re int i=1;i<=n;++i){ int pos=i; while(pos<=m&&!a[pos][i]) ++pos; if(pos>m){cout<<"No Solution";return;} if(pos!=i) swap(a[pos],a[i]); for(re int j=1;j<=m;++j) if(i!=j&&a[j][i]) a[j]^=a[i]; } for(re int i=1;i<=n;++i) cout<<a[i][0]<<'\n'; }
|