BZOJ5197:[CERC2017]Gambling Guide(最短路,期望DP)
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
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 }