高斯消元

高斯消元基本原理

模拟小学生解多元一次方程(谁家小学生解50元一次方程啊!!!

算法步骤

首先,将方程弄成一个矩阵。
例如这个方程:

2x+yz=2x+2yz=5xy+2z=7

就弄成这样一个矩阵:
211121112257

然后就开始高斯消元了 qwq
首先,我们枚举当前要消元的列k,那么除了第k个方程的列k以外的其他方程的列k都要被消元掉(当然也可以被消成倒三角形,这个等下再讲)
我们k行及以后的所有行,找到k列绝对值最大的那一行与第k行交换以减少误差和防止无解。
在实数消元的时候,我们可以将第k行全部除以一个a[k][k],这样第k个元素就是1了,方便于后面的运算。
然后再将除了第k行以外的每一行i的每一个元素a[i][j]都减去一个a[i][k]*a[k][j],这样就消去了这一行的元素k,弄完了后,第一行就只有第一个常数不为0了,以此类推,可以获得解。
不过如果是整数消元的话这样还是不行。
同样将k行换成第k列绝对值最大的那一行后,用公倍数消元(注意加还是减),使得得到一个”倒三角“
200130114288

然后 4z=8,z=2
3yz=8y=(8+z)/3 ,得到 y=2
2x+yz=2 解出 x=1

例题及代码

洛谷P3389

#include<iostream>
#include<cstdio>
#include<climits>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
double a[105][105];
int n;
bool gauss(){
    int k,i,j,bj;double t;
    for(k=0;k<n;k++){
        bj=k;
        for(i=k+1;i<n;i++)
            if(fabs(a[i][k])>fabs(a[bj][k]))bj=i;
        if(fabs(a[bj][k])<1e-8)return 0;
        for(j=k;j<=n;j++)swap(a[bj][j],a[k][j]);
        t=a[k][k];
        for(j=k;j<=n;j++)a[k][j]/=t*1.0;
        for(i=0;i<n;i++)if(k!=i){//notice:if
            t=a[i][k];
            for(j=k;j<=n;j++)a[i][j]-=a[k][j]*t*1.0;
        }
    }
    return 1;
}
int main()
{
    int i,j;
    scanf("%d",&n);
    for(i=0;i<n;i++)
        for(j=0;j<n+1;j++)scanf("%lf",&a[i][j]);
    if(gauss())for(i=0;i<n;i++)printf("%.2lf\n",a[i][n]);
    else printf("No Solution");
    return 0;
}

洛谷P2962

由于异或有交换率,所以异或方程也可以用高斯消元来解qwq
就是构造异或方程然后解啦啦

#include<iostream>
#include<cstdio>
#include<climits>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int read(){
    int q=0;char ch=' ';
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')q=q*10+ch-'0',ch=getchar();
    return q;
}
int n,m,ans=INT_MAX;
int a[40][40],x[40];
void gauss(){
    int i,j,bj,k;
    for(k=1;k<=n;k++){
        bj=k;
        for(i=k+1;i<=n;i++)if(a[i][k]){bj=i;break;}
        if(bj!=k)for(j=1;j<=n+1;j++)swap(a[bj][j],a[k][j]);
        for(i=k+1;i<=n;i++)if(a[i][k]){
            for(j=1;j<=n+1;j++)a[i][j]^=a[k][j];
        }
    }
}
void dfs(int xx,int tot){
    if(tot>ans)return;
    if(!xx){ans=min(ans,tot);return;}
    if(a[xx][xx]){
        x[xx]=a[xx][n+1];
        for(int j=n;j>xx;j--)x[xx]^=x[j]&a[xx][j];
        if(x[xx])dfs(xx-1,tot+1);
        else dfs(xx-1,tot);
    }
    else {
        x[xx]=0;dfs(xx-1,tot);
        x[xx]=1;dfs(xx-1,tot+1);
    }
}
int main()
{
    int i,j,xx,yy;
    n=read();m=read();
    for(i=1;i<=m;i++){xx=read();yy=read();a[xx][yy]=a[yy][xx]=1;}
    for(i=1;i<=n;i++)a[i][n+1]=1,a[i][i]=1;
    gauss();dfs(n,0);printf("%d",ans);
    return 0;
}

类似的题目:poj1222,洛谷P2247/codevs2311/bzoj1923(要用bitset卡常才能过)

期望与高斯消元

其实…..和普通高斯消元也差不多,就是列出期望方程然后解就可以了。

HDU4418

题目大意是有一个0,1,2…n-1,n,n-1…2,1,0这样的环,走i步的概率是p[i],求从起点走到终点的期望。
那么我们可以列出方程:f[i]= (p[k]*(f[(i+k)%n]+k))
然后把方程变形,再高斯消元即可

#include<iostream>
#include<cstdio>
#include<climits>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int T,tot,n,m,v,u,d;
int g[205],q[205];
double p[205],a[205][205],eps=1e-6;
void bfs(){
    int i,he=1,ta=1,to;
    q[he]=u;g[u]=tot++;
    while(he<=ta){
        for(i=1;i<=m;i++){
            if(fabs(p[i])<eps)continue;
            to=(q[he]+i)%n;
            if(g[to]==-1){g[to]=tot++;q[++ta]=to;}
        }
        he++;
    }
}
bool guss(){
    int i,j,k,bj;double t;
    for(k=0;k<tot;k++){
        bj=k;
        for(i=k+1;i<tot;i++)
            if(fabs(a[i][k])>fabs(a[bj][k]))bj=i;
        if(fabs(a[bj][k])<eps)return 0;
        if(bj!=k)for(j=k;j<=tot;j++)swap(a[k][j],a[bj][j]);
        t=a[k][k];
        for(j=k;j<=tot;j++)a[k][j]/=t;
        for(i=0;i<tot;i++)if(i!=k){
            if(fabs(a[i][k])<eps)continue;
            t=a[i][k];
            for(j=k;j<=tot;j++)a[i][j]-=t*a[k][j];
        }
    }
    return 1;
}
int main()
{
    int i,j;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d%d%d",&n,&m,&v,&u,&d);
        for(i=1;i<=m;i++){scanf("%lf",&p[i]);p[i]/=100.0;}
        if(u==v){printf("0.00\n");continue;}
        n=n*2-2;if(d==1&&u!=0)u=n-u;
        for(i=0;i<n;i++)g[i]=-1;
        tot=0;bfs();
        if(g[v]==-1&&g[n-v]==-1){printf("Impossible !\n");continue;}
        memset(a,0,sizeof(a));
        for(i=0;i<n;i++){
            if(g[i]==-1)continue;
            a[g[i]][g[i]]=1.0;
            if(i==v||i==n-v)continue;
            for(j=1;j<=m;j++){
                if(fabs(p[j])<eps)continue;
                int to=(i+j)%n;
                if(g[to]==-1)continue;
                a[g[i]][g[to]]-=p[j];a[g[i]][tot]+=p[j]*j;
            }
        }
        if(!guss()){printf("Impossible !\n");continue;}
        printf("%.2lf\n",a[g[u]][tot]);
    }
    return 0;
}

类似的题目:HDU2262
洛谷P3232(即bzoj3143) 题解戳这

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值