BZOJ5197【CERC2017】Gambling Guide <概率DP+Dijkstra>

Problem

【CERC2017】Gambling Guide



Description

给定一张 个点, 条双向边的无向图。
你要从 号点走到 号点。当你位于 点时,你需要花 元钱,等概率随机地买到与 相邻的一个点的票,只有通过票才能走到其它点。
每当完成一次交易时,你可以选择直接使用那张票,也可以选择扔掉那张票然后再花 元钱随机买另一张票。注意你可以无限次扔票。
请使用最佳的策略,使得期望花的钱数最少。

Input

第一行包含两个正整数 ,表示点数和边数。
接下来 行,每行两个正整数 ,表示一条双向边。
输入数据保证无重边、无自环,且 号点一定可以走到 号点。

Output

输出一行一个实数,即最少的期望花费,当绝对或者相对误差不超过 时视为正确。

Sample Input

1
2
3
4
5
6
7
8
9
5 8
1 2
1 3
1 4
2 3
2 4
3 5
5 4
2 5

Sample Output

1
4.1111111111

标签:概率DP Dijkstra

Solution

好题,精妙的 转移。

原题意相当于从 走到 ,每步可以随机一个相邻的点,选择走或不走,求期望步数。

为从 的期望步数。如果随出来的相邻点为 ,且 ,那么肯定不走最优;否则一定走到 更优。令 的出边走向的点集为 ,有

考虑以何种顺序进行这个 。最开始时,只有 ,这个值是确定的,于是我们将 加入到一个集合 中。集合 为现在“固定(fixed)”了的点的集合,在 中的点的 值一定比没在其中的点的 值小。考虑用 中固定了的 值去更新相邻的点,对于这些未被“固定”的相邻的点,如果其走向已经被”固定“的点,那么一定更优;否则,一定没有留在当前点优。
那么有

移项化简得

由于在 中的点 值一定比外面的小,已经被”固定“的点不可能因为走向没被”固定“的点而变得更优,所以一个点一旦固定了,它的 值一定固定了,我们用它去更新其他的点。每次操作时,我们选出没被固定的点中 值最小的点(因为这个值不可能再被其他没被固定的点更新了),然后用它更新周围的点。

我们发现这个过程和 单源最短路极为相似,都是每次固定一个距离最小的点,更新其周围的点。和 一样,我们可以用堆来优化找 最小的未被固定的点的过程。这样总时间复杂度

附上官方题解

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <bits/stdc++.h>
#define fir first
#define sec second
#define MAX_N 300000
#define INF 0x3f3f3f3f
using namespace std;
typedef double dnt;
typedef pair<dnt,int> pdi;
template <class T> inline void read(T &x) {
x = 0; int c = getchar(), f = 1;
for (; !isdigit(c); c = getchar()) if (c == 45) f = -1;
for (; isdigit(c); c = getchar()) (x *= 10) += f*(c-'0');
}
int n, m, sz;
dnt d[MAX_N+5], f[MAX_N+5];
dnt p[MAX_N+5], q[MAX_N+5];
int pr[MAX_N+5]; bool mrk[MAX_N+5];
struct edge {int v, nxt;} E[MAX_N<<1];
void insert(int u, int v) {E[sz] = (edge){v, pr[u]}, pr[u] = sz++;}
void addedge(int u, int v) {insert(u, v), insert(v, u), d[u]++, d[v]++;}
dnt Dijkstra() {
priority_queue <pdi> heap;
heap.push(pdi((f[n] = 0), n));
while (!heap.empty()) {
int u = heap.top().sec; heap.pop();
if (mrk[u]) continue; mrk[u] = true;
for (int i = pr[u], v; ~i; i = E[i].nxt) if (!mrk[v = E[i].v])
f[v] = ((p[v] += f[u])+d[v])/(++q[v]), heap.push(pdi(-f[v], v));
}
return f[1];
}
int main() {
read(n), read(m);
memset(pr, -1, sizeof pr);
for (int i = 1, u, v; i <= m; i++)
read(u), read(v), addedge(u, v);
return printf("%lf\n", Dijkstra()), 0;
}
------------- Thanks For Reading -------------
0%