• 算法学习————高斯消元


    其实高斯消元就和我们正常解方程一样,n个未知数,至少n个方程

    整体代入法,每一个方程消去一个未知数,最后化成一个三角形

    然后从最后一个方程把未知数一个一个解出来代入到前面的方程中

    高斯消元步骤

    高斯消元法在将增广矩阵化为最简形后对于自由未知量的赋值,需要掌握线性相关知识,且赋值存在人工经验的因素,使得在学习过程中有一定的困难,将高斯消元法划分为五步骤,从而提出五步骤法,内容如下:

    1. 增广矩阵行初等行变换为行最简形;

    2. 还原线性方程组;

    3. 求解第一个变量;

    4. 补充自由未知量;

    5. 列表示方程组通解。

    解的判断

    无解:当消元完毕后,发现有一行系数都为 0,但是常数项不为 0,此时无解

    多解:当消元完毕后,发现有多行系数、常数项均为 0,此时多解,有几行为全为 0,就有几个自由元,即变量的值可以任取,有无数种情况可以满足给出的方程组

    看网上解析还有什么模线性方程组,异或方程组,可能没有机会学了吧

    列主元:为了更好的保留精度,我们可以每次把绝对值最大的一行换上来

    例题:[USACO10HOL]Driving Out the Piggies G

    图上高斯消元也算是一类经典问题吧,好像树上可以不用,但是我还没有学

    求每个点经过次数的概率:

    设f[i]表示点i被经过的期望次数,值得注意的是最开始1号点就被经过了1次

    (f_i = sumlimits_{(i,j)in E} f_j imes frac{1}{deg_j} imes (1-frac{p}{q}))

    代码:

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <cmath>
    #define B cout<<"Breakpoint"<<endl;
    #define O(x) cout<<#x<<" "<<x<<endl;
    #define o(x) cout<<#x<<" "<<x<<" ";
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const int maxn = 305;
    int n,m,p,q;
    int deg[maxn],edge[maxn][maxn];
    double A[maxn][maxn],P;
    void Gauss(int rowline,int colline,double a[][maxn]){
    	for (int i = 1;i <= rowline;i++){
    		int rowId = i;
    		for (int j = i+1;j <= rowline;j++){
    			if (fabs(a[j][i]) > fabs(a[rowId][i])) rowId = j;
    		}
    		if (rowId != i){
    			for (int j = i;j <= colline+1;j++) swap(a[i][j],a[rowId][j]);
    		}
    		for (int j = i+1;j <= rowline;j++){
    			double tmp = a[j][i]/a[i][i];
    			for (int k = i;k <= colline+1;k++){
    				a[j][k] -= a[i][k]*tmp;
    			}
    		}
    	}
    	for (int i = rowline;i >= 1;i--){
    		for (int j = i+1;j <= colline;j++){
    			a[i][colline+1] -= a[i][j]*a[j][colline+1];
    		}
    		a[i][colline+1] /= a[i][i];
    	}
    	for (int i = 1;i <= rowline;i++) printf("%.9lf
    ",a[i][colline+1]*P);
    }
    int main(){
    	n = read(),m = read(),p = read(),q = read();
    	P = 1.0*p/q;
    	for (int i = 1;i <= m;i++){
    		int x = read(),y = read();
    		edge[x][y] = 1,edge[y][x] = 1;
    		deg[x]++,deg[y]++;
    	}
    	for (int i = 1;i <= n;i++){
    		A[i][i] = 1.0;
    		for (int j = 1;j <= n;j++){
    			if (edge[i][j])	A[i][j] -= (1.0-P)/deg[j];
    		}
    	}
    	A[1][n+1] = 1.0;
    	Gauss(n,n,A);
    	return 0;
    }
    

    如果给出一个式子(f_i = 1+sumlimits_{(i,j)in E} frac{f_j}{deg_i}),可以发现他互相依赖就需要用到高斯消元

    但是如果我告诉你是在树上,并且叶子节点为1呢?

    (f_i = k_if_{fa_i}+b_i),我们不难发现b中只有常数项和i的儿子

    DP儿子j时,(f_j)可以用(k_jf_i+b_j)表示出,然后就能得到一个只与(f_i)(f_{fa_i})有关的方程

    把他的儿子代入到他里面变成(f_i = 1+frac{f_{fa_i}sumlimits_{(i,j)in EAnd j eq fa_i}k_jf_i+sumlimits_{(i,j)in EAnd j eq fa_i}b_j}{deg_i})

    最后(f_i = frac{f_{fa_i}}{deg_i-sumlimits_{(i,j)in EAnd j eq fa_i} k_j} + frac{deg_i+sumlimits_{(i,j)in EAnd j eq fa_i} b_j}{deg_i -sumlimits_{(i,j)in EAnd j eq fa_i} k_j})

    实际上我们不需要把每次的f求出来,只需要维护k和b就好

    代码:

    void dfs(int x,int fa){
    	if (deg[x] == 1){
    		k[x] = 0,b[x] = a[x];
    		return;
    	}
    	for (int i = head[x];i;i = ed[i].nxt){
    		int to = ed[i].to;
    		if (to == fa) continue;
    		dfs(to,x);
    		k[x] -= k[to],b[x] += b[to];
    	} 
    	k[x] = qpow(k[x],mod-2),b[x] = b[x]*k[x];
    }
    
  • 相关阅读:
    Visual Studio 编译使用 Trilinos中 Belos库 求解线性方程组 (待续)
    Visual Studio编译使用CLAPACK
    Visual Studio 页面文件太小,无法完成操作
    wpf中的BringToFront
    想买的书
    远程桌面使用matlab报错License Manager Error -103的解决办法
    工厂模式-理解Spring的Bean工厂(马士兵经典例子)
    面向对象、继承封装、多态小例子(马士兵)
    检查pod版本及更新pod
    mac之开发中的环境配置
  • 原文地址:https://www.cnblogs.com/little-uu/p/14978586.html
Copyright © 2020-2023  润新知