「BZOJ 3270」博物館「高斯消元」
阿新 • • 發佈:2019-02-08
否則 一個人 高斯消元 name 題意 spa pac swa scanf
應該算高斯消元經典題了吧。
題意:一個無向連通圖,有兩個人分別在\(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; }
「BZOJ 3270」博物館「高斯消元」