「BZOJ 3270」博物馆「高斯消元」

应该算高斯消元经典题了吧。

题意:一个无向连通图,有两个人分别在\(s,t\),若一个人在\(u\),每一分钟有\(p[u]\)的概率不动,否则随机前往一个相邻的结点,求在每个点相遇的概率

题解

首先求一个\(mov[i]=\frac{1-p[i]}{deg[i]}\)表示结点i每次移动到某个相邻结点的概率,\(deg[i]\)表示结点\(i\)的度

为了方便,我们把每个点向自己连条边,下面写式子好些(注意度数不能增加)

然后考虑设计状态\(f(a,b)\)表示第一个人在\(a\),第二个人在\(b\)的概率

\[f(a,b)=\sum_{(u,a),(v,b),u\not =v}g(u,a)g(v,b) \]

其中\(g(a,b)\)表示\(a\)\(b\)的概率,当\(a=b\)时为\(p[a]\),否则为\(mov[a]\)

然后把二元组映射到大小为\(n^2\)的一维数组,高斯消元,注意\(f(s,t)=1\)

时间复杂度:\(O(n^6)\)

#include <algorithm>
#include <cstdio>
#include <vector>
#include <cmath>
using namespace std;

const int N = 25;

int n, m, s, t, deg[N];
double mov[N], p[N];
vector<int> G[N];

double calc(int u, int v) {
    return u == v ? p[u] : mov[u];
}

int pos(int u, int v) {
    return (u - 1) * n + v;
}

double a[N * N][N * N];

void gauss(int n) {
    for(int i = 1, j = 1; i <= n; j = ++ i) {
        for(int k = i + 1; k <= n; k ++) if(fabs(a[j][i]) < fabs(a[k][i])) j = k;
        if(i != j) for(int k = i; k <= n + 1; k ++) swap(a[j][k], a[i][k]);
        for(j = i + 1; j <= n; j ++) {
            double z = a[j][i] / a[i][i];
            for(int k = i; k <= n + 1; k ++) a[j][k] -= z * a[i][k];
        }
    }
    for(int i = n; i >= 1; i --) {
        for(int j = i + 1; j <= n; j ++) a[i][n + 1] -= a[j][n + 1] * a[i][j];
        a[i][n + 1] /= a[i][i];
    }
}

int main() {
    scanf("%d%d%d%d", &n, &m, &s, &t);
    for(int u, v, i = 1; i <= m; i ++) {
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
        deg[u] ++; deg[v] ++;
    }
    for(int i = 1; i <= n; i ++) {
        scanf("%lf", p + i);
        G[i].push_back(i);
        mov[i] = (1 - p[i]) / deg[i];
    }
    int k = n * n;
    for(int i = 1; i <= n; i ++) {
        for(int j = 1; j <= n; j ++) {
            int p = pos(i, j); a[p][p] = -1;
            if(i == s && j == t) a[p][k + 1] = -1;
            for(int x = 0; x < G[i].size(); x ++) {
                int u = G[i][x];
                for(int y = 0; y < G[j].size(); y ++) {
                    int v = G[j][y];
                    if(u == v) continue ;
                    a[p][pos(u, v)] += calc(u, i) * calc(v, j);
                }
            }
        }
    }
    gauss(k);
    for(int i = 1; i <= n; i ++)
        printf("%.6f ", a[pos(i, i)][k + 1]);
    return 0;
}

posted @ 2019-02-08 17:14  hfhongzy  阅读(241)  评论(0编辑  收藏  举报