1. 程式人生 > >【bzoj5197】[CERC2017]Gambling Guide 期望dp+堆優化Dijkstra

【bzoj5197】[CERC2017]Gambling Guide 期望dp+堆優化Dijkstra

要花 當前 tdi rac for 直接 期望dp 隨機 p值

題目描述

給定一張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{\sum\limits_{(x,y)}\text{min}(f[x],f[y])+1}{d[x]}$ ,其中 $d[x]$ 表示 $x$ 的度數。

套路:對於這種 “初始只有一個點的dp值確定、其它點的dp值與其相鄰的點有關” 的圖上dp,考慮使用類似最短路的方式轉移。

初始的時候除了 $n$ 以外,每個點的 $\text{min}(f[x],f[y])$ 都取 $f[x]$ ,dp值為 $+\infty$ 。

然後從 $n$ 號點開始最短路轉移:對於當前的點 $i$ ,如果某個相鄰的 $j$ 有 $f[j]>f[i]$ ,則對於 $f[j]$ 的計算來說,$\text{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(m\log 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\n" , f[1]);
	return 0;
}

【bzoj5197】[CERC2017]Gambling Guide 期望dp+堆優化Dijkstra