zoukankan      html  css  js  c++  java
  • [数论]高斯消元学习笔记

    (Q:)高斯消元是什么?听起来好高级啊??

    (A:)二元一次方程组解过吗?那就是高斯消元。

    • 高斯消元((Gauss)),是一种用来求解线性方程组的方法,在(OI)竞赛中广泛使用。

    首先对高斯消元做一些准备:

    (Q:)什么是线性方程组?

    (A:)鸡兔同笼方程组(M)(N)元一次方程所构成的方程组。

    为了简化表达,做出如下定义:

    • 系数矩阵

      把线性方程组未知数的系数写成一个(M imes N)的矩阵,即为“系数矩阵”。

    • 增广矩阵

      把线性方程组等号右边的常数写成一个(M imes 1)的矩阵,拼在系数矩阵右边,即为一个大小为(M imes (N+1))的“增广矩阵”。

    (Example:)

    假如现有一方程组:

    [egin{cases}x_1+x_2+4x_3=17\5x_1+x_2+4x_3=25\8x_1+x_2+0x_3=19end{cases} ]

    其系数矩阵即为:

    [egin{bmatrix}1&1&4\5&1&4\8&1&0end{bmatrix} ]

    (???)

    增广矩阵为:

    [egin{bmatrix}1&1&4&17\5&1&4&25\8&1&0&19end{bmatrix} ]

    (Q:)这些很简单啊,接下来要干什么呢?

    (A:)接着我们对方程组进行求解,包括如下(3)个步骤:

    • 对其中一个方程乘以一个非(0)常数

    • 用一个方程加上另一个方程

    • 交换两个方程

    称以上操作为矩阵的“初等行变换”。

    (Q:)这些毫无意义的语言 都是什么意思啊?


    以一个二元一次方程组为例:

    [egin{cases}x_1+9x_2=21quad(1)\9x_1+x_2=29quad(2)end{cases} ]

    增广矩阵:

    [egin{bmatrix}1&9&21\9&1&29end{bmatrix} ]


    • 对其中一个方程乘以一个非(0)常数:

    ((1)*=-9:)

    [egin{cases}-9x_1+-81x_2&=-189quad(1)\9x_1+x_2&=29quad(2)end{cases} ]

    增广矩阵:

    [egin{bmatrix}-9&-81&-189\9&1&29end{bmatrix} ]


    • 用一个方程加上另一个方程

    ((1)+=(2):)

    [egin{cases}-80x_2&=-160\9x_1+x_2&=29end{cases} ]

    [egin{cases}x_2&=2\9x_1+x_2&=29end{cases} ]

    增广矩阵:

    [egin{bmatrix}0&1&2\9&1&29end{bmatrix} ]


    • 交换两个方程

    (Swap((1),(2)):)

    [egin{cases}9x_1+x_2&=29\x_2&=2end{cases} ]

    增广矩阵:

    [egin{bmatrix}9&1&29\0&1&2end{bmatrix} ]


    怎么样,是不是很简单啊?

    初等行变换后的矩阵被称为“阶梯型矩阵”(是不是很像?并没有)。

    系数部分称为“上三角矩阵”。

    最后从下到上更新一遍即可求出方程组的解。

    另外,若在求解过程中出现了(0=a)的情况,则方程组无解。

    若过程中无法消元(找不到一个方程,(x_1sim x_{i-1})系数为(0)(x_i)系数不为(0)),则解不唯一,称此类(x_i)为自由元。

    时间复杂度 (O(n^3))

    空间复杂度 (O(n^2))

    接下来是实际应用。


    P3389 【模板】高斯消元法

    这就是裸的模板题了。

    时间复杂度 (O(n^3))

    空间复杂度 (O(n^2))

    代码:

    #include <cstdio>
    #include <algorithm>
    
    inline double Abs(double x){return x>=0?x:-x;}
    
    int n;
    double a[105][105];
    const double Eps=1e-7;//精度视情况而定,不要太小或太大
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=n+1;++j)
    			scanf("%lf",&a[i][j]);//共同构成增广矩阵
    	for(int i=1;i<=n;++i)//进行n次消元,消去第i项未知数
    	{
    		for(int j=1;j<=n;++j)//找一个x[i]系数不为0的方程进行消元
    			if(Abs(a[j][i])>Eps)
    				for(int k=1;k<=n+1;++k)
    					std::swap(a[i][k],a[j][k]);
    		if(Abs(a[i][i])<=Eps)return puts("No Solution"),0;//出现自由元,无解
    		//消去其他方程x[i]系数
    		for(int j=1;j<=n;++j)
    			if(i!=j)
    			{
    				double Rate=a[j][i]/a[i][i];//相对比例
    				for(int k=1;k<=n+1;++k)a[j][k]-=a[i][k]*Rate;//消元
    			}
    	}
    	for(int i=1;i<=n;++i)printf("%.2f
    ",a[i][n+1]/a[i][i]);
    	return 0;
    }
    

    P4035 [JSOI2008]球形空间产生器

    稍微转换一下题目

    设已知点为(a_i),求一个点((x_1,x_2,dots,x_n)),使得存在常数(Dis),满足

    [forall iin[1,n+1],sum_{1le jle n}(a_{i,j}-x_j)^2=Dis ]

    这样就有了(n+1)个方程,但发现无法求解。

    考虑构造(n)(n)元一次方程组进行高斯消元。

    用相邻的两个方程做差,得:

    [sum_{1le jle n}(a_{i,j}-x_j)^2-sum_{1le jle n}(a_{i+1,j}-x_j)^2=Dis-Dis ]

    [sum_{1le jle n}(a_{i,j}-x_j)^2-(a_{i+1,j}-x_j)^2=0 ]

    [sum_{1le jle n}2(a_{i,j}-a_{i+1,j})x_j=sum_{1le jle n}(a_{i,j}^2-a_{i+1,j}^2) ]

    高斯消元即可。

    时间复杂度 (O(n^3))

    空间复杂度 (O(n^2))

    代码:

    #include <cmath>
    #include <cstdio>
    #include <algorithm>
    
    const double Eps=1e-8;
    
    int n;
    double p[15][15],b[15],c[15][15];
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n+1;++i)
            for(int j=1;j<=n;++j)
                scanf("%lf",&p[i][j]);
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
            {
                c[i][j]=(p[i][j]-p[i+1][j])*2;
                b[i]+=p[i][j]*p[i][j]-p[i+1][j]*p[i+1][j];//这里把系数和常数分开放
            }
        for(int i=1;i<=n;++i)
        {
            for(int j=i;j<=n;++j)
                if(fabs(c[j][i])>Eps)
                {
                    for(int k=1;k<=n;++k)std::swap(c[i][k],c[j][k]);
                    std::swap(b[i],b[j]);
                }
            for(int j=1;j<=n;++j)
            {
                if(i==j)continue;
                double Rate=c[j][i]/c[i][i];
                for(int k=i;k<=n;++k)c[j][k]-=c[i][k]*Rate;
                b[j]-=b[i]*Rate;
            }
        }
        for(int i=1;i<=n;++i)
            printf("%.3f%c",b[i]/c[i][i],i==n?'
    ':' ');
        return 0;
    }
    

    难题就不继续探讨了,毕竟我也不会

    参考资料:

    各省省选题目

    《算法竞赛进阶指南》 -- 李煜东

  • 相关阅读:
    1451. Rearrange Words in a Sentence
    1450. Number of Students Doing Homework at a Given Time
    1452. People Whose List of Favorite Companies Is Not a Subset of Another List
    1447. Simplified Fractions
    1446. Consecutive Characters
    1448. Count Good Nodes in Binary Tree
    709. To Lower Case
    211. Add and Search Word
    918. Maximum Sum Circular Subarray
    lua 时间戳和时间互转
  • 原文地址:https://www.cnblogs.com/LanrTabe/p/10293291.html
Copyright © 2011-2022 走看看