zoukankan      html  css  js  c++  java
  • 高斯消元学习

    高斯消元:

    求解线性方程组的方法。

    主体:将系数提出来,形成一个系数矩阵。将等号右边的常数提出来,形成一个常数矩阵。然后加减消元,带入消元。

    步骤:

    1.明确要消去的元的位置pos,将某一行有这个元(即系数不为0)的方程提出来,这一行记作 i ,对应的元的系数记作x。

    2.现在把第 i 行去消第 j 行(目的是把第 j 行的pos处系数变为0),记原系数为y。将第 i 行同 ÷ y,再*x ,就构造出了pos处系数为x的另一个方程。

    3.两式相减,就可以把pos处消成0啦。

    最后答案:

    通过带入消元法最后处理出的是一个阶梯矩阵(对角线上有值),答案即为常数÷对角线上的值啦。

    那有没有无穷多解或无解的方程呢?

    1.当0=d是说明无解

    2.当存在一行所有的系数都为0,即有无穷多个解,缺少的那个位置为自由元,可以取任意值,其他 值可以固定 的变量叫主元。 这样的方程组有无穷多解。

    参考:算法与竞赛指南:P155~160

    //待补图!!!

    模板:P3389 【模板】高斯消元法

    //P3389 【模板】高斯消元法 
    #include<bits/stdc++.h>
    using namespace std;
    #define ri register int
    #define N 105
    #define eps 1e-8
    double b[N],c[N][N];
    int main()
    {
        int n;
        scanf("%d",&n);
        for(ri i=1;i<=n;i++){
            for(ri j=1;j<=n;j++)
            scanf("%lf",&c[i][j]);//系数矩阵 
            scanf("%lf",&b[i]);//常数矩阵 
        }
        for(int i=1;i<=n;i++){
            for(int j=i;j<=n;j++)
             if(fabs(c[j][i])>eps) swap(c[i],c[j]),swap(b[i],b[j]);//把非0的一行去消其他行 swap使得其变成当前行
            //找到一个系数全为0的式子 说明存在自由元 方程有无穷多个解 
            if(fabs(c[i][i])<eps) { printf("No Solution
    "); return 0; }
            for(int j=1;j<=n;j++){//这里枚举行 将每一行的第i个数以后的数加减消元 
                if(i==j) continue;
                double r=c[j][i]/c[i][i];//通分 
                for(int k=i;k<=n;k++) c[j][k]-=r*c[i][k];//第j行的每一个数消去对应的值 
                b[j]-=b[i]*r;//常数项也要变 
            }
        }
        for(int i=1;i<=n;i++) printf("%.2lf
    ",b[i]/c[i][i]);
    }
    /*
    3
    1 2 -1 3
    1 2 -4 0
    -1 -2 6 2
    */

    变式:P4035 [JSOI2008]球形空间产生器

    (只是根据题意处理了一下系数矩阵和常数矩阵而已)

    #include<bits/stdc++.h>
    using namespace std;
    #define N 15
    #define ri register int 
    #define eps 1e-8
    int n;
    double a[N][N],c[N][N],b[N];
    int main()
    {
        scanf("%d",&n);
        for(ri i=1;i<=n+1;++i)
         for(ri j=1;j<=n;++j) scanf("%lf",&a[i][j]);
        for(ri i=1;i<=n;++i)
         for(ri j=1;j<=n;++j){
             c[i][j]=2*(a[i+1][j]-a[i][j]);
             b[i]+=a[i+1][j]*a[i+1][j]-a[i][j]*a[i][j];
        }
        for(ri i=1;i<=n;++i){
            for(ri j=i;j<=n;++j)
            if(fabs(c[j][i])>eps) swap(c[j],c[i]),swap(b[j],b[i]);
            for(ri j=1;j<=n;++j){
                if(i==j) continue;
                double r=c[j][i]/c[i][i];
                for(ri k=i;k<=n;++k) c[j][k]-=c[i][k]*r;
                b[j]-=b[i]*r;
            }
        }
        for(ri i=1;i<=n;++i) printf("%.3lf ",b[i]/c[i][i]);
    }
    /*
    2
    0.0 0.0
    -1.0 1.0
    1.0 0.0
    */
    View Code

    求异或方程模板:开关问题

    分析:
    最难的地方在于灯的关联性
    因为每一个开关只能操作一次, 就可以把一个灯是否操作看做0/1的变量, 通过列方程来求解。
    而一个开关关联了另一个开关, 即意味着在将原始状态变成理想状态的过程中 要xi^xj 。
    问题就转换成:有n个方程, 每个方程有n个异或值:a(i,j)^xj 。       

    另外:a(i,i)=1自己与自己肯定是相关联的) 。
    解释一下每个变量的意义:在第i个方程中, 第j个是否与i关联:a(i,j) ,求它是否会被操作:xj 。
    而它们的常数项为:st[i]^ed[i], 即初始状态^期望状态 。
    在输入的时候就预处理出系数矩阵 ,然后把每一个方程二进制压位:注意最后一位是=右边的常数项 。
    答案统计:
    这道题求的是方案数,很明显最后的操作序列中可能会存在这样的数:取0也可以, 取1也可以。
    这样的数就是普通高斯消元中的自由元,由于每个数都有0和1两种选择 ,则答案为2^cnt ,cnt为自由元的个数 。
    注意代码具体实现中的细节

    //#include<bits/stdc++.h>
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    #define ri register int
    #define N 55
    int a[N],ans=0,n;
    /*void get(int x)//输出对应的二进制数 
    {
        int tmp[10],cnt=0;
        memset(tmp,0,sizeof(tmp));
        while(x){
            cnt++;
            if(x&1) tmp[cnt]=1;
            x>>=1;
        }
        for(int i=n+1;i>=1;i--) printf("%d",tmp[i]);
        printf("
    ");
    }*/
    int main()
    {
        int T,x,y;
        scanf("%d",&T);
        while(T--)
        {
            memset(a,0,sizeof(a));
            scanf("%d",&n);
            for(ri i=1;i<=n;++i) scanf("%d",&a[i]);
            for(ri i=1;i<=n;++i) scanf("%d",&x),a[i]^=x,a[i]|=(1<<i);
            while(scanf("%d%d",&x,&y)){
                if(x==0 && y==0) break;
                a[y]|=(1<<x);//这里y和x不要弄反!!!
                //a[i]表示的是 二进制下 一系列操作的异或值 能取到最低位的0/1 的系数矩阵
                //而x y 表示操作x y会随之变化
                //即在使第y个灯变成最终值时 方程中第x位的系数是1 所以x和y不能反着写!!! 
            }
            ans=1;
            for(ri i=1;i<=n;++i){
                for(ri j=i+1;j<=n;++j) 
                if(a[j]>a[i]) swap(a[i],a[j]);//取的是首位最大值的a[i] 
                if(a[i]==0) { ans=1<<(n-i+1); break; }//即全部为0 说明剩下的方程系数全为0 即有n-i+1个自由元 
                if(a[i]==1) { ans=-1; break; }//最后一项=1 前面都=0 即出现了无解的情况 
                for(ri k=n;k>=1;--k)
                if(a[i]>>k & 1){//每次找到其最高位 去消其他的方程 
                    for(ri j=1;j<=n;++j)
                    if( i!=j && a[j]>>k & 1) a[j]^=a[i];//只消第k位有值的方程
                    //原因:每一次固定消最高位 如果其它位这一位为0 说明没有这个未知数 是不应该被消的 
                    break;//!!!
                }
            }
            if(ans==-1) printf("Oh,it's impossible~!!
    ");
            else printf("%d
    ",ans);
        }
    }
  • 相关阅读:
    用AB对Webservice做压力测试
    web压力测试工具(小而精)
    让IIS支持10万并发
    数据库之间数据转换最快方法
    C#中执行Dos命令
    .NET 4.0中使用内存映射文件实现进程通讯
    HTML解析利器HtmlAgilityPack
    C#快速找出磁盘内的所有文件
    ORM for Net主流框架汇总与效率测试
    新浪微博基于MySQL的分布式数据库实践
  • 原文地址:https://www.cnblogs.com/mowanying/p/11502409.html
Copyright © 2011-2022 走看看