高斯消元专题

[高斯消元专题]

最近学了高斯消元,理解起来非常滴简单,就是像我们平常解方程组一样的加减消元,代入消元什么的。但是为了使得这个算法变的比较好梳理,能按照一定的套路去打,那就简单多了。

那么,为了方便得出算法的写法与最终求解的写法,我们一般会将一个方程组一一对应转为矩阵(转为矩阵是什么意思?就是把每一个方程的每一项的系数取下来(包括最后的常数项),然后一一对应的放置在一个矩阵里面)后变成下述形式:

显然,如果某些方程组(矩阵)呈现出这样的形式的话,我们就非常容易能把它解出来。

但是如果初始的方程组不是这样的形式,那怎么办?

还记得加减消元和代入消元不?这样两个方程之间至少能消去一个未知数。

那么,我们做n^3次,连续的这样做消元(当然也要有顺序和策略,这是消元的核心),就能形成上述形式的矩阵。

当然,我们一般选择消成上三角形的矩阵,效率更高更好写。

形成这样的形式后,我们然后从下向上一层一层回代就能求出所以未知数的值了(当然还有无解和多解的情况)。

那么,如何得到一个上三角矩阵呢?每次仅对当前行下面的元素消元。如样例(用最小公倍数法):

[2]  3  1  16

[1] -2 -2 -7

0   -1  7  18

|             |

|             |

\/           \/

(原1式-2*原2式=新2式)

2  3  1  16

0  [7]  3   30

0  [-1]  7   18

|             |

|             |

\/           \/
(原2式+7*原3式=新3式)

2  3  1    16

0  7  3   30

0  0  52  156

得出x(3)=3,再遍历第二行,将x(3)=3代入,得7x(2)+3*3=30,解出x(2)=3,再代入第一行,得2*x(1)+3*3+3=16,x(1)=2。

下面是几个与方程有关的问题(转自“诚叙”的cnblogs)

Q1:方程无解怎么判断?

A1:如果有一行方程的所有变量的系数都为0,而常数项不为0,方程当然就无解啦。

Q2:方程多解是什么状况呢?

A2:如果有一行方程的所有变量的系数都为0,常数项也为0,那么这就说明出现了自由元(就是在这个方程中不受约束可以自由取值的未知数),且自由元的个数=全部为0的行数,这样就会导致方程多解了。

Q3:方程唯一解是什么样子呢?

A3:不是上面两个不就是了么.....好吧,其实表现在矩阵上是完整美好标准的上三角矩阵或标准型矩阵。

Q4:您上面都说的是解线性的实数或正整数解的方程,但是在题目中经常碰到解异或方程组,这该怎么解呢?

A4:是啊是啊~异或方程组是经常可以在题目中见到的,例如很经典的开关问题....使用的思想还是我们上面讨论的加减消元的思想,转化的矩阵也是我们上面的矩阵,只是矩阵中的系数通常只有0和1了,表示的是如果改变列元素是否会对行元素有影响,例如打开开关1可以让灯2和灯3改变状态,那么a(1,2)=a(1,3)=1,这样就可以通过2,3的状态,求出是不是改变了开关1。(可能还是不太懂....就是我们列的方程成了已知灯的前后状态,求解这些开关是怎么用的,那么我们的已知量就是灯与开关的关系(系数)&灯的状态(常数项),未知量是开关是否使用,大致是一个a(1,1)*x[1] xor a(1,2)*x[2] xor...xor a(1,n)*x[n] 来求解x[1..n]的过程,还是看不懂就看我的另一篇做题的博客好啦)

上面大概介绍了下方程的建立方法,那么我们在解方程的时候我们的消元方法就要变成异或消元了,利用的是1 xor 1=0,如果你要解某个变量 i 就找到一行 i 的系数不为0的交换到当前行(如果当前行都没有这个元素,就解不了了(上面普通方程求解的时候也可以这样)),然后找到其他当前变量的系数不为0的,异或之(每个系数都异或,包括常数),最后也能化成标准或上三角矩阵,从而求解。

还要特殊说明的是在异或方程中如果知道了方程的自由元数量就可以知道总共有多少组解啦!因为只有1或0两种可能嘛,所以答案就是2^n(n为自由元的数量)

Q5:高斯消元还有什么用呢?

A5:还可以和期望概率扯上关系呢,因为一个事件的期望值很可能和与它相关的其他事件扯上关系,例如我从原点出发,每次只能走一步,往上或往右,那么走到(i,j)的期望步数就是从(i-1,j)走来的期望步数*从(i-1,j)走来的概率+1再加上从(i,j-1)走来的期望步数*从(i,j-1)走来的概率+1,是不是很像方程组呢.....通过这样也能建立方程组(....啊啊啊其实我开始有点口糊的感觉了....我还没打过这方面的题~加油你们去膜大神们吧~moto_No.1还讲了个在线求高斯,大家可以去看看他的博客,概率什么的,查查题目就能找到啦(有趣的“驱赶猪猡”....))

这位dalao写的很不错哦~~~

那么,下面就给出几份不同情况的代码:

对于普通的整数方程组(非实数):

 1 int Gauss(int equ,int var){
 2     int R=1,C=1;
 3     for (; R<=equ&&C<=var; R++,C++){
 4         int Mx=R;
 5         for (int i=R+1; i<=equ; i++)
 6             if (abso(a[i][C])>abso(a[Mx][C])) i=Mx;
 7         if (Mx!=R)
 8             for (int j=C; j<=var+1; j++) swap(a[Mx][j],a[R][j]);
 9         if (a[R][C]==0){R--; continue;}
10         for (int i=R+1; i<=equ; i++) if (a[i][C]!=0){
11             int LCM=lcm(abso(a[i][C]),abso(a[R][C]));
12             int tx=LCM/abso(a[i][C]),ty=LCM/abso(a[R][C]);
13             if (a[i][C]*a[R][C]<0) ty=-ty;
14             for (int j=C; j<=var+1; j++) a[i][j]=a[i][j]*tx-a[R][j]*ty;
15         }
16     }
17     for (int i=R; i<=equ; i++) if (a[i][var+1]!=0) return -1; //无解的情况
18     if (R<=var) return var-R+1; //多解的情况
19     for (int i=n; i; i--){
20         int res=a[i][var+1];
21         for (int j=i+1; j<=var; j++) if (a[i][j]!=0) res-=a[i][j]*x[j];
22         x[i]=res/a[i][i];
23     }
24     return 0; //唯一解的情况
25 }

对于某些01异或方程组(给出的是有唯一解的情况,多解和无解想必也不难):

 1 void Gauss(){
 2     int row=1,col=1;
 3     for (; row<=N&&col<=N; row++,col++){
 4         int Mx_r=row;
 5         for (int i=row+1; i<=N; i++)
 6             if (abso(a[i][col])>abso(a[Mx_r][col])) Mx_r=i;
 7         if (Mx_r!=row)
 8             for (int j=col; j<=N+1; j++) swap(a[Mx_r][j],a[row][j]);
 9         for (int i=row+1; i<=N; i++) if (a[i][col])
10             for (int j=col; j<=N+1; j++) a[i][j]^=a[row][j];
11     }
12     for (int i=N; i>=1; i--){
13         x[i]=a[i][N+1];
14         for (int j=i+1; j<=N; j++) x[i]^=(a[i][j]&&x[j]);
15     }
16 }

对于某些同余方程组(模数必须为质数):

 1 int Gauss(int equ,int var){
 2     int R=1,C=1;
 3     for (; R<=equ&&C<=var; R++,C++){
 4         int Mx=R;
 5         for (int i=R+1; i<=equ; i++)
 6             if (abso(a[i][C])>abso(a[Mx][C])) Mx=i;
 7         if (Mx!=R)
 8             for (int j=C; j<=var+1; j++) swap(a[Mx][j],a[R][j]);
 9         if (a[R][C]==0){R--; continue;}
10         for (int i=R+1; i<=equ; i++) if (a[i][C]!=0){
11             int LCM=lcm(abso(a[i][C]),abso(a[R][C]));
12             int tx=LCM/abso(a[i][C]),ty=LCM/abso(a[R][C]);
13             if (a[i][C]*a[R][C]<0) ty=-ty;
14             for (int j=C; j<=var+1; j++)
15                 a[i][j]=(((a[i][j]*tx-a[R][j]*ty)%TT)+TT)%TT;
16         }
17     }
18     for (int i=R; i<=equ; i++) if (a[i][var+1]!=0) return -1;
19     if (R<=var) return var-R+1;
20     for (int i=n; i; i--){
21         int res=a[i][var+1];
22         for (int j=i+1; j<=var; j++)
23             if (a[i][j]!=0) res-=a[i][j]*x[j],res=((res%TT)+TT)%TT;
24         x[i]=((res*Q_pow(a[i][i],TT-2))%TT+TT)%TT;// x[i]=res/a[i][i]求逆元
25     }
26     return 0;
27 }

对于某些实数方程组(唯一解情况):

 1 void Gauss(int equ,int var){
 2     int R=1,C=1;
 3     for (; R<=equ&&C<=var; R++,C++){
 4         int Mx=R;
 5         for (int i=R+1; i<=equ; i++)
 6             if (dcmp(dabs(a[i][C])-dabs(a[Mx][C]))==1) Mx=i;
 7         if (Mx!=R)
 8             for (int j=C; j<=var+1; j++) swap(a[Mx][j],a[R][j]);
 9         if (dcmp(a[R][C])==0){R--; continue;}
10         for (int i=R+1; i<=equ; i++) if (dcmp(a[i][C])!=0){
11             double k=a[i][C]/a[R][C];
12             for (int j=C; j<=var+1; j++) a[i][j]=a[i][j]-k*a[R][j];
13         }
14     }
15     for (int i=var; i; i--){
16         double res=a[i][var+1];
17         for (int j=i+1; j<=var; j++) res-=a[i][j]*x[j];
18         x[i]=res/a[i][i];
19     }
20 }

对于某些实数方程组多解的情况(求出哪些是自由元,以及非自由元的值):

 1 int Gauss(int equ,int var){
 2     int R=1,C=1;
 3     for (; R<=equ&&C<=var; R++,C++){
 4         int Mx=R;
 5         for (int i=R+1; i<=equ; i++)
 6             if (dcmp(dabs(a[i][C])-dabs(a[Mx][C]))==1) Mx=i;
 7         if (Mx!=R)
 8             for (int j=C; j<=var+1; j++) swap(a[Mx][j],a[R][j]);
 9         if (dcmp(a[R][C])==0){R--; continue;}
10         for (int i=R+1; i<=equ; i++) if (dcmp(a[i][C])!=0){
11             double k=a[i][C]/a[R][C];
12             for (int j=C; j<=var+1; j++)
13                 a[i][j]=a[i][j]-k*a[R][j];
14         }
15     }
16     for (int i=R; i<=equ; i++) if (dcmp(a[i][var+1])!=0) return -1;
17     if (R<=var){
18         for (int i=R-1; i; i--){
19             int num=0,idx;
20             for (int j=1; j<=var; j++) if (freee[j]&&dcmp(a[i][j])!=0){
21                 num++,idx=j;
22             }
23             if (num!=1) continue;
24             double res=a[i][var+1];
25             for (int j=1; j<=var; j++) if (j!=idx)
26             if (dcmp(a[i][j])!=0) res-=a[i][j]*x[j];
27             x[idx]=res/a[i][idx],freee[idx]=0;
28         }
29         return var-R+1;
30     }
31     for (int i=1; i<=var; i++) freee[i]=0;
32     for (int i=var; i; i--){
33         double res=a[i][var+1];
34         for (int j=i+1; j<=var; j++) res-=a[i][j]*x[j];
35         x[i]=res/a[i][i];
36     }
37     return 0;
38 }

当然还有其他的情况,那代码其实并不重要了,重要的是我们要能够举一反三。->_->

下面是习题:

时间: 2024-08-26 00:36:38

高斯消元专题的相关文章

poj_1222_高斯消元

第一次学习使用高斯消元,将灯板化为线性方程组,进行求解. /*######################################################################### # File Name: poj_1222.cpp # Author: CaoLei # Created Time: 2015/7/20 15:48:04 ###################################################################

HDU 4870 Rating(高斯消元)

HDU 4870 Rating 题目链接 题意:一个人注册两个账号,初始rating都是0,他每次拿低分的那个号去打比赛,赢了加50分,输了扣100分,胜率为p,他会打到直到一个号有1000分为止,问比赛场次的期望 思路:f(i, j)表示i >= j,第一个号i分,第二个号j分时候,达到目标的期望,那么可以列出转移为f(i, j) = p f(i', j') + (1 - p) f(i'' + j'') + 1 f(i', j')对应的是赢了加分的状态,f(i'', j'')对应输的扣分的状态

【BZOJ 4171】 4171: Rhl的游戏 (高斯消元)

4171: Rhl的游戏 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 74  Solved: 33[Submit][Status][Discuss] Description RHL最近迷上一个小游戏:Flip it.游戏的规则很简单,在一个N*M的格子上,有一些格子是黑色,有一些是白色 .每选择一个格子按一次,格子以及周围边相邻的格子都会翻转颜色(边相邻指至少与该格子有一条公共边的格子 ),黑变白,白变黑.RHL希望把所有格子都变成白色的.不幸

POJ 1830 开关问题 高斯消元,自由变量个数

http://poj.org/problem?id=1830 如果开关s1操作一次,则会有s1(记住自己也会变).和s1连接的开关都会做一次操作. 那么设矩阵a[i][j]表示按下了开关j,开关i会被操作一次,记得a[i][i] = 1是必须的,因为开关i操作一次,本身肯定会变化一次. 所以有n个开关,就有n条方程, 每个开关的操作次数总和是:a[i][1] + a[i][2] + ... + a[i][n] 那么sum % 2就代表它的状态,需要和(en[i] - be[i] + 2) % 2

BZOJ 3105: [cqoi2013]新Nim游戏 [高斯消元XOR 线性基]

以后我也要用传送门! 题意:一些数,选择一个权值最大的异或和不为0的集合 终于有点明白线性基是什么了...等会再整理 求一个权值最大的线性无关子集 线性无关子集满足拟阵的性质,贪心选择权值最大的,用高斯消元判断是否和已选择的线性相关 每一位记录pivot[i]为i用到的行 枚举要加入的数字的每一个二进制为1的位,如果有pivot[i]那么就异或一下(消元),否则pivot[i]=这个数并退出 如果最后异或成0了就说明线性相关... #include <iostream> #include &l

[bzoj1013][JSOI2008]球形空间产生器sphere-题解[高斯消元]

Description 有一个球形空间产生器能够在n维空间中产生一个坚硬的球体.现在,你被困在了这个n维球体中,你只知道球面上n+1个点的坐标,你需要以最快的速度确定这个n维球体的球心坐标,以便于摧毁这个球形空间产生器. Input 第一行是一个整数n(1<=N=10).接下来的n+1行,每行有n个实数,表示球面上一点的n维坐标.每一个实数精确到小数点后6位,且其绝对值都不超过20000. Output 有且只有一行,依次给出球心的n维坐标(n个实数),两个实数之间用一个空格隔开.每个实数精确到

[spoj104][Highways] (生成树计数+矩阵树定理+高斯消元)

In some countries building highways takes a lot of time... Maybe that's because there are many possiblities to construct a network of highways and engineers can't make up their minds which one to choose. Suppose we have a list of cities that can be c

UVA 10828 Back to Kernighan-Ritchie(高斯消元)

高斯消元求概率 对于非起点,期望x[i] = ∑x[j] / deg[j] #include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<string> #include<algorithm> #include<map> #include<queue> #include<vector> #includ

【BZOJ-1923】外星千足虫 高斯消元 + xor方程组

1923: [Sdoi2010]外星千足虫 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 766  Solved: 485[Submit][Status][Discuss] Description Input 第一行是两个正整数 N, M. 接下来 M行,按顺序给出 Charles 这M次使用“点足机”的统计结果.每行包含一个“01”串和一个数字,用一个空格隔开.“01”串按位依次表示每只虫子是否被放入机器:如果第 i 个字符是“0”则代表编号为