• [数论]高斯消元学习笔记


    (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;
    }
    

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

    参考资料:

    各省省选题目

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

  • 相关阅读:
    8.9乘船问题
    8.8几个背包问题
    8.7贪心策略例题:字典序最小问题
    8.6贪心策略例题:区间覆盖问题
    8.5贪心策略例题:区间选点问题
    8.4贪心策略例题:区间调度问题
    SQL 报表 生成月份临时表
    CentOS8 .NET Core项目部署
    Django with database on SQL Server
    SQL 父子表,显示表中每条记录所在层级
  • 原文地址:https://www.cnblogs.com/LanrTabe/p/10293291.html
Copyright © 2020-2023  润新知