Description
有一个 n n n个点 m m m条边的无向图,两个人分别从 x , y x,y x,y出发,每个人每分钟有 p i p_i pi的概率不动, 有 1 − p i 1−p_i 1−pi的概率走到随机一个相邻的点。
当他们在同一时刻选择前往同一个房间, 他们就会在那个房间相遇并停止。
求在每个点相遇的概率。
n ≤ 22 n \leq 22 n≤22
Solution
这道题有两种做法。
第一种做法,枚举终点,然后高斯消元即可。直接这样做是 O ( n 7 ) O(n^7) O(n7)的,显然无法通过。考虑每次修改终点只会修改 f ( i , i ) f(i,i) f(i,i)的值,所以可以把 f ( i , i ) f(i,i) f(i,i)视为常数,那么列出方程组后高斯消元可以得出 f ( i , j ) = ∑ k = 1 n A i , j , k f ( k , k ) f(i,j)=\sum_{k=1}^n A_{i,j,k} f(k,k) f(i,j)=∑k=1nAi,j,kf(k,k)。每次枚举终点暴力计算即可。
第二种做法,设 f ( i , j ) f(i,j) f(i,j)表示第一个人在 i i i第二个人在 j j j的期望出现次数,由于终止状态 ( i , i ) (i,i) (i,i)的出现次数只可能为 0 / 1 0/1 0/1,所以概率即期望。直接解方程即可。
注意初始点的方程为
f ( a , b ) − 1 = ∑ i = 1 n ∑ j = 1 n A ( i , j ) f ( i , j ) f(a,b)-1=\sum_{i=1}^n\sum_{j=1}^n A(i,j)f(i,j) f(a,b)−1=i=1∑nj=1∑nA(i,j)f(i,j)
其余点的方程为
f ( a , b ) = ∑ i = 1 n ∑ j = 1 n A ( i , j ) f ( i , j ) f(a,b)=\sum_{i=1}^n\sum_{j=1}^n A(i,j)f(i,j) f(a,b)=i=1∑nj=1∑nA(i,j)f(i,j)
其中 A ( i , j ) A(i,j) A(i,j)表示从 ( a , b ) (a,b) (a,b)转移到 ( i , j ) (i,j) (i,j)的概率。
#include <bits/stdc++.h>
using namespace std;
inline int gi()
{
char c = getchar();
while(c < '0' || c > '9') c = getchar();
int sum = 0;
while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
const double eps = 1e-7;
const int maxn = 25, maxm = maxn * maxn;
int n, m, a, b, deg[maxn], Id[maxn][maxn];
vector<int> to[maxn];
double p[maxn], mat[maxm][maxm], ans[maxm], f[maxn][maxn];
void pre()
{
m = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
Id[i][j] = ++m;
mat[Id[a][b]][m + 1] = -1;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) {
int x = Id[i][j];
--mat[x][x];
if (i != j) mat[x][x] += p[i] * p[j];
for (int u : to[i])
for (int v : to[j]) {
if (u == v) continue;
mat[x][Id[u][v]] = (1 - p[u]) * (1 - p[v]) / deg[u] / deg[v];
}
for (int u : to[i]) {
if (u == j) continue;
mat[x][Id[u][j]] = (1 - p[u]) * p[j]/ deg[u];
}
for (int v : to[j]) {
if (v == i) continue;
mat[x][Id[i][v]] = p[i] * (1 - p[v]) / deg[v];
}
}
}
int dcmp(double a)
{
if (-eps < a && a < eps) return 0;
else if (a > 0) return 1;
else return -1;
}
/*int gauss()
{
for (int i = 1; i < m; ++i) {
int p = i;
for (int j = i + 1; j <= m; ++j)
if (abs(mat[j][i]) > abs(mat[p][i])) p = j;
if (p != i)
for (int j = i; j <= m + 1; ++j) swap(mat[i][j], mat[p][j]);
for (int j = i + 1; j <= m; ++j) {
if (!dcmp(mat[j][i])) continue;
double t1 = mat[j][i], t2 = mat[i][i];
for (int k = i; k <= m + 1; ++k) mat[j][k] -= mat[i][k] * t1 / t2;
}
}
for (int i = m; i >= 1; --i) {
if (!dcmp(mat[i][i])) return 0;
for (int j = i + 1; j <= m; ++j)
mat[i][m + 1] -= ans[j] * mat[i][j];
ans[i] = mat[i][m + 1] / mat[i][i];
}
return 1;
}*/
void gauss()
{
for (int i = 1; i <= m; ++i) {
int p = i;
for (int j = i; j <= m; ++j)
if (abs(mat[j][i]) > abs(mat[p][i])) p = j;
if (p != i)
for (int j = i; j <= m; ++j) swap(mat[p][j], mat[i][j]);
for (int j = 1; j <= m; ++j) {
if (p == j) continue;
double t = mat[j][i] / mat[i][i];
for (int k = i; k <= m + 1; ++k) mat[j][k] -= mat[i][k] * t;
}
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
f[i][j] = mat[Id[i][j]][m + 1] / mat[Id[i][j]][Id[i][j]];
}
int main()
{
n = gi(); m = gi(); a = gi(); b = gi();
for (int u, v, i = 1; i <= m; ++i) {
u = gi(); v = gi();
to[u].push_back(v); ++deg[u];
to[v].push_back(u); ++deg[v];
}
for (int i = 1; i <= n; ++i) scanf("%lf", &p[i]);
pre();
gauss();
for (int i = 1; i <= n; ++i) printf("%.10lf ", f[i][i]);
return 0;
}