高斯消元学习

高斯消元:

求解线性方程组的方法。

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

步骤:

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\n"); 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\n",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("\n");
}*/
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~!!\n");
        else printf("%d\n",ans);
    }
}

 

转载于:https://www.cnblogs.com/mowanying/p/11502409.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值