题意:给定一张图,边是双向的,2个人在图上走,对于每个点,在某个时刻有pi的概率停留在这个点,有1-pi的概率 等概率走向它周围的点,要求对于每个点i,这2个人在点i相遇的概率,注意此题在边上相遇不算相遇。
这题真是太神奇了,在我校集训队大爷InvUsr的讲解下终于是搞明白了,
对于每个状态,记f[i][j]表示a走到点i,b走到点j,期望经过多少个点,那么对于每个f[i][j],有(Σf[i][i])=1,并且对于每个点,在这个点相遇的概率和期望成正比,所以我们得出f[i][i]就是在i点相遇的期望
然后要列出dp方程,分4个情况考虑,一种是停留在原地,有2种是其中一个点走,另一个点不走,还有就是2个点一起走,列方程的时候注意假如f[i][j]由f[i'][j']转移,那么i'!=j'。
然后边界条件是f[a][b]=1,因为一开始是在a,b所以期望经过至少1个点
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 405;
double f[MAXN][MAXN], p[MAXN];
int n, m, i, j, k, x, y, z, begix, begiy, du[MAXN];
bool g[MAXN][MAXN];
int main()
{
cin >> n >> m >> begix >> begiy;
for(i = 1; i <= m; i ++)
cin >> x >> y, g[x][y] = g[y][x] = 1, du[x] ++, du[y] ++;
for(i = 1; i <= n; i ++)
cin >> p[i];
int N = n * n;
for(i = 1; i <= n; i ++)
for(j = 1; j <= n; j ++)
{
int num = (i - 1) * n + j, nexnum;
if (i != j) f[num][num] = 1.0 - p[i] * p[j];
else f[num][num] = 1.0;
for(x = 1; x <= n; x ++)
if (g[i][x] && x != j)
{
nexnum = (x - 1) * n + j;
f[num][nexnum] -= p[j] * (1.0 - p[x]) / du[x];
}
for(y = 1; y <= n; y ++)
if (g[j][y] && i != y)
{
nexnum = (i - 1) * n + y;
f[num][nexnum] -= p[i] * (1.0 - p[y]) / du[y];
}
for(x = 1; x <= n; x ++)
for(y = 1; y <= n; y ++)
if (g[i][x] && g[j][y] && x != y)
{
nexnum = (x - 1) * n + y;
f[num][nexnum] -= (1 - p[x]) / du[x] * (1 - p[y]) / du[y];
}
}
f[(begix - 1) * n + begiy][N + 1] = 1;
for(i = 1; i <= N; i ++)
{
int k = 0;
for(j = i; j <= N; j ++)
if (fabs(f[j][i]) >= 1e-9) {k = j; break;}
if (!k) continue;
for(j = 1; j <= N + 1; j ++)
swap(f[i][j], f[k][j]);
double sb = f[i][i];
for(j = 1; j <= N + 1; j ++)
f[i][j] /= sb;
for(j = 1; j <= N; j ++)
if (i != j && fabs(f[j][i]) >= 1e-9)
{
sb = f[j][i];
for(k = 1; k <= N + 1; k ++)
f[j][k] -= f[i][k] * sb;
}
}
for(i = 1; i <= n; i ++)
printf("%.6lf ", f[(i - 1) * n + i][N + 1]);
}