[SDOI2006] 线性方程组
主要记录一下高斯消元解方程的正确写法
希望能:
1.判断唯一解、无穷解、无解
2.求出自由元个数
3.求出哪些是自由元
思路就是
1.用vis标记记录每个方程是不是固定的(如果这个方程的某一个未知数的系数用来当“代表”来消其他行的系数,那么这个方程就算是固定的,不要动了,否则会乱)
2.每次找xi系数不是0的且没有vis的方程,找到的话换到第i行,找不到就算了。
3.消除的时候,把每一行的xi系数都消了,不管是不是vis
这样得到的上三角矩阵里面,如果a[i][i]为0,那么必然这一行所有未知数系数都是0,因为xj的系数会被其他行消掉(如果没有被消掉,说明其他行xj都是0,那么xj会被选为j的代表,这个方程放到第j行,而不是第i行。)
所以这时如果某行未知数系数都是0,且等式右侧常数非零,则无解;
如果所有行未知数系数都是0的右侧常数也为0,那么无穷多组解。每个这样子的行都是一个自由元,且自由元就是xi
代码:
#include<bits/stdc++.h> using namespace std; typedef long double lb;//用double也能过 const int N=52; const lb eps=1e-8;//实测eps设为0.01都可以过 int n; lb f[N][N]; lb ans[N]; int fl; bool vis[N];//这个vis表示这一行的向量有没有固定(假如这一行作为某次选定的行,用来消除别的行的未知数,那么这一行就不能动了,vis设为1 int guass(){ for(int i=1;i<=n;i++){ int id=i; bool find=false; for(int j=1;j<=n;j++){//这里从第一行开始找。找所有vis是0的情况。 if(vis[j]==0&&fabs(f[j][i])>fabs(f[id][i])){ id=j; } } if(fabs(f[id][i])>=eps) find=true;//find=true表示找到了一个未固定的且第i个未知数系数不为0的行 if(id!=i){//换到第i行去,为了好看。 for(int j=1;j<=n+1;j++){ swap(f[id][j],f[i][j]); } } if(find){//找到 vis[i]=true;//要用来消其他的行,这一行就固定住,不要动了,要不就乱了 for(int j=1;j<=n;j++){//这里从1开始循环,把xi系数非0的行全消一遍 if(j==i) continue; lb ch=f[j][i]/f[i][i]; for(int k=1;k<=n+1;k++){ f[j][k]=f[j][k]-ch*f[i][k]; } } } } //判断有没有解 int wq=0,wj=0; for(int i=1;i<=n;i++) { int j=1; while(fabs(f[i][j])<eps&&j<=n+1)j++; if(j>n+1)wq=1;//如果这一行全是0,那么这一个未知数是自由元,先把wq设为1,(但是不是无穷取决于会不会之后无解) if(j==n+1)wj=1; //这一行除了等式右边都是0,那么无解,wj设为1 } if(wj){return -1;}//先判断无解 if(wq){return 0;}//不是无解且wq为1的话,那么是无穷多组解。 for(int i=n;i>=1;i--){//倒着推导每一个未知数 for(int j=1;j<=n;j++){ if(i==j) continue; f[i][n+1]-=f[i][j]*ans[j]; } ans[i]=f[i][n+1]/f[i][i]+eps; } return 1; } int main() { scanf("%d",&n);lb k; for(int i=1;i<=n;i++) { for(int j=1;j<=n+1;j++){ scanf("%Lf",&f[i][j]); } } fl=0; fl=guass(); if(fl!=1) { printf("%d",fl);return 0; } for(int i=1;i<=n;i++){ printf("x%d=%.2Lf\n",i,ans[i]); } return 0; }