BZOJ5197:[CERC2017]Gambling Guide(最短路,期望DP)

时间:2021-11-01 10:58:01

Description

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

Input

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

Output

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

Sample Input

5 8
1 2
1 3
1 4
2 3
2 4
3 5
5 4
2 5

Sample Output

4.1111111111

Solution

最优策略的话,一个点只会走向到终点期望步数比他小的点,用最短路来更新$DP$就可以了。

反正我也说不太明白,感性理解一下吧。

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<queue>
 5 #define N (300009)
 6 #define pi pair<double,int>
 7 using namespace std;
 8 
 9 struct Edge{int to,next;}edge[N<<1];
10 int n,m,u,v,deg[N],c[N],vis[N];
11 int head[N],num_edge;
12 double s[N],f[N];
13 priority_queue<pi,vector<pi>,greater<pi> >q;
14 
15 void add(int u,int v)
16 {
17     deg[v]++;
18     edge[++num_edge].to=v;
19     edge[num_edge].next=head[u];
20     head[u]=num_edge;
21 }
22 
23 int main()
24 {
25     scanf("%d%d",&n,&m);
26     for (int i=1; i<=m; ++i)
27         scanf("%d%d",&u,&v), add(u,v), add(v,u);
28     q.push(pi(0,n));
29     while (!q.empty())
30     {
31         int x=q.top().second; q.pop();
32         if (vis[x]) continue; vis[x]=1;
33         for (int i=head[x]; i; i=edge[i].next)
34         {
35             int y=edge[i].to;
36             if (vis[y]) continue;
37             c[y]++; s[y]+=f[x]; f[y]=(s[y]+deg[y])/c[y];
38             q.push(pi(f[y],y));
39         }
40     }
41     printf("%.10lf\n",f[1]);
42 }