bzoj1415 [Noi2005]聪聪和可可【概率dp 数学期望】

时间:2021-07-10 04:48:14

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1415

noip2016 D1T3,多么痛的领悟。。。看来要恶补一下与期望相关的东西了。

这是一道经典的求期望的题,尽管我的代码里把那个记忆化搜索那个叫做dp,但事实上这不是动态规划,只是递推。

先预处理出x[i][j],表示聪聪在i,可可在j时,下一步聪聪到达的顶点标号,f[i][j]是那张记忆化搜索的表,表示聪聪在i,可可在j时,期望所需的时间,out[i]表示i点的出度(其实就是度啦,无向图没什么入度出度的)。显然,下一个单位时间聪聪的位置是x[x[i][j]][j],而可可是在所有与j相邻的节点或者j自己中随机挑一个到达,每个的概率都是1 / (out[j] + 1),所以有

f[i][j] = ( sigma(  f[x[x[i][j]][j]][k]  ) + f[x[x[i][j]][j]][j] ) / (out[j] + 1) + 1,其中k表示每个与j相邻的节点。

然后写一个记忆化搜索就完事咯~

#include <cstdio>
#include <cstring> const int maxn = 1005, maxe = 1005; int n, e, ini_neko, ini_mouse, t1, t2;
int head[maxn], to[maxe << 1], next[maxe << 1], lb, out[maxn];
int que[maxn], head_, tail, h, x[maxn][maxn], d[maxn][maxn];
double f[maxn][maxn]; inline void ist(int aa, int ss) {
to[lb] = ss;
next[lb] = head[aa];
head[aa] = lb;
++out[aa];
++lb;
}
double dp(int i, int j) {
if (f[i][j] != -1.0) {
return f[i][j];
}
if (i == j) {
return f[i][j] = 0.0;
}
if (x[i][j] == j) {
return f[i][j] = 1.0;
}
if (x[x[i][j]][j] == j) {
return f[i][j] = 1.0;
}
f[i][j] = 0.0;
for (int k = head[j]; k != -1; k = next[k]) {
f[i][j] += dp(x[x[i][j]][j], to[k]);
}
f[i][j] = (f[i][j] + dp(x[x[i][j]][j], j)) / (double)(out[j] + 1) + 1;
return f[i][j];
} int main(void) {
//freopen("in.txt", "r", stdin);
memset(head, -1, sizeof head);
memset(next, -1, sizeof next);
for (int i = 0; i < maxn; ++i) {
for (int j = 0; j < maxn; ++j) {
f[i][j] = -1.0;
}
}
scanf("%d%d", &n, &e);
scanf("%d%d", &ini_neko, &ini_mouse);
while (e--) {
scanf("%d%d", &t1, &t2);
ist(t1, t2);
ist(t2, t1);
} memset(d, -1, sizeof d);
for (int i = 1; i <= n; ++i) {
memset(que, 0, sizeof que);
head_ = tail = 0;
que[tail++] = i;
d[i][i] = 0;
while (head_ != tail) {
h = que[head_++];
for (int j = head[h]; j != -1; j = next[j]) {
if (d[i][to[j]] == -1) {
d[i][to[j]] = d[i][h] + 1;
que[tail++] = to[j];
}
}
}
} memset(x, 0x3c, sizeof x);
for (int i = 1; i <= n; ++i) {
for (int k = head[i]; k != -1; k = next[k]) {
for (int j = 1; j <= n; ++j) {
if (d[i][j] == d[to[k]][j] + 1 && x[i][j] > to[k]) {
x[i][j] = to[k];
}
}
}
} printf("%.3f\n", dp(ini_neko, ini_mouse));
return 0;
}