zoukankan      html  css  js  c++  java
  • 【bzoj5197】[CERC2017]Gambling Guide 期望dp+堆优化Dijkstra

    题目描述

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

    输入

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

    输出

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

    样例输入

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

    样例输出

    4.1111111111


    题解

    期望dp+堆优化Dijkstra

    设 $f[i]$ 表示 $i$ 到终点的期望步数,那么有:$f[n]=0 , f[x]=frac{sumlimits_{(x,y)} ext{min}(f[x],f[y])+1}{d[x]}$ ,其中 $d[x]$ 表示 $x$ 的度数。

    套路:对于这种 “初始只有一个点的dp值确定、其它点的dp值与其相邻的点有关” 的图上dp,考虑使用类似最短路的方式转移。

    初始的时候除了 $n$ 以外,每个点的 $ ext{min}(f[x],f[y])$ 都取 $f[x]$ ,dp值为 $+infty$ 。

    然后从 $n$ 号点开始最短路转移:对于当前的点 $i$ ,如果某个相邻的 $j$ 有 $f[j]>f[i]$ ,则对于 $f[j]$ 的计算来说,$ ext{min}(f[j],f[i])$ 取 $f[i]$ 更优。此时更新 $j$ 的dp值,并将 $j$ 加入到待用于更新其它点的集合中。

    注意到:如果使用 $f[i]$ 将 $f[j]$ 更新为 $f'[j]$ ,那么显然有 $f[i]le f'[j]le f[j]$ (等号在 $f[i]=f[j]$ 时取到),满足堆优化Dijkstra的贪心策略(当前最小的一定不会再被更新到更小),因此可以使用dp值小根堆来维护待用于更新其它点的集合,使用类似堆优化Dijkstra的方式转移即可。

    最终的答案就是 $f[1]$ 。

    时间复杂度 $O(mlog n)$ 

    #include <queue>
    #include <cstdio>
    #include <algorithm>
    #define N 300010
    using namespace std;
    typedef pair<double , int> pr;
    priority_queue<pr> q;
    double s[N] , f[N];
    int head[N] , to[N << 1] , next[N << 1] , cnt , vis[N] , d[N] , c[N];
    inline void add(int x , int y)
    {
    	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
    }
    int main()
    {
    	int n , m , i , x , y;
    	scanf("%d%d" , &n , &m);
    	for(i = 1 ; i <= m ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x) , d[x] ++ , d[y] ++ ;
    	q.push(pr(0 , n));
    	while(!q.empty())
    	{
    		x = q.top().second , q.pop();
    		if(vis[x]) continue;
    		vis[x] = 1;
    		for(i = head[x] ; i ; i = next[i])
    			if(!vis[to[i]])
    				c[to[i]] ++ , s[to[i]] += f[x] , f[to[i]] = (s[to[i]] + d[to[i]]) / c[to[i]] , q.push(pr(-f[to[i]] , to[i]));
    	}
    	printf("%lf
    " , f[1]);
    	return 0;
    }
    
  • 相关阅读:
    day25:接口类和抽象类
    vue1
    How the weather influences your mood?
    机器学习实验方法与原理
    How human activities damage the environment
    Slow food
    Brief Introduction to Esports
    Massive open online course (MOOC)
    Online learning in higher education
    Tensorflow Dataset API
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8507379.html
Copyright © 2011-2022 走看看