zoukankan      html  css  js  c++  java
  • [基本操作]高斯消元

    高斯消元,就是 $O(n^3)$ 解方程组

    bzoj3270 博物馆

    一个无向图,两个人一个在 A 一个在 B,这两个人开始随机走,求这两个人在每个点相遇的概率(在边上不会相遇),每个点有一个自环,每次有 $P_i$ 的概率走自环,剩下 $1 - P_i$ 的概率等概率选一个相邻点走过去

    $n leq 20$

    sol:

    我是不是...开错题了?

    原题等价于求这两个人在每个点相遇的期望步数

    设 $f_{(i,j)}$ 为第一个人在 $i$ ,第二个人在 $j$ 的期望步数,枚举这两个人走的情况转移

    注意:

    1.$i==j$ 的点不转移,因为相遇了就停了

    2.因为一开始就在起点,所以起点的概率要 $+1$ ,概率 $+1$ 相当于方程解出来右边是 $-1$

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    inline int read()
    {
        int x = 0,f = 1;char ch = getchar();
        for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f;
        for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
        return x * f;
    }
    const int maxn = 410;
    int n,m,A,B;
    int d[maxn];double pr[maxn],a[maxn][maxn];
    vector<int> G[maxn];
    int mem[25][25],dfn;
    inline int f(int x,int y){return mem[x][y] ? mem[x][y] : (mem[x][y] = ++dfn);}
    void gauss()
    {
        int now=1,tot = n * n;
        for(int i=1;i<=tot;i++)
        {
            int j;
            for(j=now;!a[j][now]&&j<=tot;j++);
            for(int k=1;k<=tot+1;k++)swap(a[now][k],a[j][k]);
            for(int j=1;j<=tot;j++)
                if(j!=now)
                {
                    double t=a[j][now]/a[now][now];
                    for(int k=1;k<=tot+1;k++)
                        a[j][k]-=t*a[now][k];
                }
            now++;
        }
    } 
    int main()
    {
        n = read(),m = read(),A = read(),B = read();
        for(int i=1;i<=n;i++)G[i].push_back(i);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)f(i,j);
        a[f(A,B)][n * n + 1] = -1;
        for(int i=1;i<=m;i++)
        {
            int u = read(),v = read();
            d[u]++;G[u].push_back(v);
            d[v]++;G[v].push_back(u); 
        }
        for(int i=1;i<=n;i++)cin >> pr[i];
        for(int x=1;x<=n;x++)
            for(int y=1;y<=n;y++)
            {
                a[f(x,y)][f(x,y)]--;
                for(int i=0;i<G[x].size();i++)
                    for(int j=0;j<G[y].size();j++)
                    {
                        int tx = G[x][i],ty = G[y][j];
                        int p = f(x,y),np = f(tx,ty);
                        if(tx == ty)continue;
                        if(tx == x && ty == y)a[p][np] += pr[x] * pr[y];
                        else if(tx == x && ty != y)a[p][np] += pr[x] * (1 - pr[ty]) / d[ty];
                        else if(tx != x && ty == y)a[p][np] += (1 - pr[tx]) / d[tx] * pr[y];
                        else if(tx != x && ty != y)a[p][np] += (1 - pr[tx]) / d[tx] * (1 - pr[ty]) / d[ty];
                    }
            }
        gauss();
        for(int i=1;i<=n;i++)
        {
            int fd = f(i,i);
            printf("%.6lf ",a[fd][n * n + 1] / a[fd][fd]);
        }
    }
    View Code

    bzoj2337 XOR 和路径

    一个无向图,从 $1$ 出发每次随机选一条出边走,走到 $n$ 停止,求经过每条边权异或和的期望

    $n leq 100$

    sol:

    XOR 的期望...XOR 看起来是整数,期望看起来是小数,没法直接算(两个小数没法 XOR )

    可以拆开每一位,算出每一位最终结果的期望,再按位值加起来就可以了

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    inline int read()
    {
        int x = 0,f = 1;char ch = getchar();
        for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f;
        for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
        return x * f;
    }
    const int maxn = 310;
    int n,m;
    int d[maxn];double a[maxn][maxn];
    int first[maxn],to[maxn * maxn],nx[maxn * maxn],val[maxn * maxn],cnt;
    inline void add(int u,int v,int w)
    {
        to[++cnt] = v;
        nx[cnt] = first[u];
        first[u] = cnt;
        val[cnt] = w;
        d[u]++;
    }
    void gauss()
    {
        int now=1,tot = n;
        for(int i=1;i<=tot;i++)
        {
            int j;
            for(j=now;!a[j][now]&&j<=tot;j++);
            for(int k=1;k<=tot+1;k++)swap(a[now][k],a[j][k]);
            for(int j=1;j<=tot;j++)
                if(j!=now)
                {
                    double t=a[j][now]/a[now][now];
                    for(int k=1;k<=tot+1;k++)
                        a[j][k]-=t*a[now][k];
                }
            now++;
        }
    } 
    int main()
    {
        n = read(),m = read();
        for(int i=1;i<=m;i++)
        {
            int u = read(),v = read(),w = read();
            add(u,v,w);
            if(u != v)add(v,u,w);
        }double ans = 0;
        for(int T=0;T<=30;T++)
        {
            memset(a,0,sizeof(a));
            for(int i=1;i<n;i++)a[i][i] = 1.0;
            for(int x=1;x<n;x++)
            {
                for(int i=first[x];i;i=nx[i])
                {
                    if(val[i] & (1 << T))a[x][to[i]] += (1.0 / d[x]),a[x][n + 1] += (1.0 / d[x]);
                    else a[x][to[i]] -= (1.0 / d[x]);
                }
            }a[n][n] = 1;
            gauss();
            ans += (1 << T) * (a[1][n + 1] / a[1][1]);
        }
        printf("%.3lf
    ",ans);
    }
    View Code

    bzoj4820 硬币游戏

    给出 $n$ 个长度均为 $m$ 的不同 $01$ 串,随机生成一个无限长的 $01$ 串,对 $n$ 个 $01$ 串中的每个,求出它最先在随机串中出现的概率.

    $n,m leq 300$

    sol:

    如果直接上 AC 自动机的话,会有 $O(n^2)$ 个方程,所以要考虑优化方程个数

    可以把“没有任何给定 $01$ 串出现”作为一个状态 $N$,如果一个 $01$ 串 $A$,然后某次转移从 $N$ 转移到了 $N + A$,那么 $A$ 就有可能获胜

    至于“有可能”是因为有可能 $A$ 还没有出现完,别的就已经出现了

    这时候就要考虑处理出这种情况发生的概率

    容易知道这种情况发生是因为 $A$ 的前缀等于 $B$ 的后缀

    之后我们可以对于每一对 $(i,j)$ 做一次 kmp ,求出所有 $S_i$ 和 $S_j$ 的相同前后缀

    然后概率就是 $P_{(i,j)} = sumlimits_k 2^{k-m}$

    设走到 $N$ 的期望步数是 $x_0$,走到第 $i$ 个人的终止节点的期望步数是 $x_i$,则可以对每一个人列一个方程

    $2^{-m} space x_0 space = space x_i+sumlimits_{j=1}^n P_{(i,j)} space x_j$

    最后还有一个方程 $sumlimits_{i=1}^n x_i = 1$ (因为最后总是要有人赢的)

    这样就是 $n+1$ 个方程,高斯消元即可

    #include <bits/stdc++.h>
    #define LL long long
    #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i)
    #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i)
    using namespace std;
    inline int read() {
        int x = 0, f = 1;char ch;
        for (ch = getchar(); !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
        for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0';
        return x * f;
    }
    const int maxn = 310;
    int n, m;
    int fail[maxn << 1];
    double f[maxn][maxn];
    char ch[maxn][maxn], a[maxn << 1];
    void kmp() {
        int j = 0; int len = strlen(a + 1);
        rep(i, 2, len) {
            while(j && a[j + 1] != a[i]) j = fail[j];
            if(a[j + 1] == a[i]) j++;
            fail[i] = j;
        }
    }
    void gauss() {
        int now = 1;
        for(int i=1;i<=n+1;i++) {
            int j;
            for(j=now;!f[j][now] && j<=n+1;j++);
            swap(f[now], f[j]);
            for(int j=1;j<=n+1;j++) {
                if(j != now) {
                    double t = f[j][now] / f[now][now];
                    for(int k=1;k<=n+2;k++) f[j][k] -= t * f[now][k];
                }
            }
            now++;
        }
    }
    int main() {
        n = read(), m = read();
        rep(i, 1, n) scanf("%s", ch[i] + 1);
        rep(i, 1, n) rep(j, 1, n) {
            rep(k, 1, m) a[k] = ch[i][k], a[k + m] = ch[j][k];
            kmp(); for(int k = fail[m << 1]; k; k = fail[k]) if(k < m) f[i][j] += pow(0.5, m - k);
        }
        rep(i, 1, n) f[i][i] += 1.0, f[i][n+1] -= 1.0; f[n + 1][n + 2] = 1.0;
        rep(i, 1, n) f[n + 1][i] = 1.0; gauss();
        rep(i, 1, n) printf("%.10f
    ", f[i][n + 2] / f[i][i]);
    }
    //2019.3.11 终于填上了...
    View Code

    UPD:真正的高斯消元

    大概就是从左到右考虑每一个变量,找到系数最大的一行当主元,换到第 $i$ 行

    然后这一行从第 $i$ 个开始每个除以 $mat(i,i)$

    之后从第 $i+1$ 行到第 $n$ 行,第 $i$ 个到第 $n+1$ 个开始每个减去 $mat(j,i) div mat(i,i) imes mat(i,k)$

    这样能消成一个下三角矩阵,把主对角线乘起来就是行列式

    求答案的话搞一个 $ans$ 数组,一开始 $ans[i] = mat(i,n+1)$

    之后从 $n-1$ 到 $1$ 枚举 $i$,$ans[i] = ans[i] - sumlimits_{j=i+1}^n ans[j] imes mat(i,j)$

    最后 $ans[i]$ 就是 $i$ 的值

    (然而大概是要用来求行列式吧,之前的 Gauss-Jordan 解方程似乎更好,然而 Gauss-Jordan 行列式简直吔屎

    #include <bits/stdc++.h>
    #define LL long long
    using namespace std;
    #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i)
    #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i)
    inline int read() {
        int x = 0, f = 1; char ch = getchar();
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
        for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0';
        return x * f;
    }
    int n;
    double a[110][110], ans[110];
    int Gauss() {
        rep(i, 1, n) {
            int now = i;
            rep(k, i+1, n) if(fabs(a[k][i]) > fabs(a[now][i])) now = k;
            if(a[now][i] == 0) return 1;
            if(now != i) rep(k, i, n+1) swap(a[i][k], a[now][k]);
            double t = a[i][i];
            rep(j, i, n+1) a[i][j] /= t;
            rep(j, i+1, n) {
                double t = a[j][i] / a[i][i];
                rep(k, i, n+1) a[j][k] -= t * a[i][k];
            }
        }
        return 0;
    }
    int main() {
        n = read();
        rep(i, 1, n) rep(j, 1, n+1) cin >> a[i][j];
        if(Gauss()) cout << "No Solution" << endl;
        else {
            rep(i, 1, n) ans[i] = a[i][n+1];
            dwn(i, n-1, 1)
                rep(j, i+1, n)
                    ans[i] -= a[i][j] * ans[j];
            rep(i, 1, n) cout << fixed << showpoint << setprecision(2) << ans[i] << endl;
        }
    }
    View Code

    UPD:新板子

    int Gauss() {
        int i, j;
        for(i = 1, j = 1; i <= n && j <= m; ++j) {
            int pos = 0;
            rep(k, i, n) if(a[k][j]) { pos = k; break; }
            if(!pos) continue;
            if(pos != i) rep(k, 1, m) swap(a[i][k], a[pos][k]);
            int _inv = ksm(a[i][j], mod - 2);
            rep(k, i+1, n) if(a[k][j]) {
                int tmp = 1LL * a[k][j] * _inv % mod;
                rep(l, j, m) (a[k][l] -= (1LL * tmp * a[i][l] % mod)) %= mod;
            } i++;
        } return i - 1;
    }
    View Code
  • 相关阅读:
    vue中的样式
    v-model 双向数据绑定
    v-on 事件修饰符
    Linq Join
    Elasticsearch.Net 异常:[match] query doesn't support multiple fields, found [field] and [query]
    MongoDB开启权限认证
    elasticsearch备份与恢复
    elasticserach + kibana环境搭建
    Kibana TypeError : Object #<GlobalState> has no method 'setDefaults'
    匿名函数
  • 原文地址:https://www.cnblogs.com/Kong-Ruo/p/10182284.html
Copyright © 2011-2022 走看看