2017-2018 ACM-ICPC, Central Europe Regional Contest (CERC 17) G - Gambling Guide(期望dp)
阿新 • • 發佈:2018-12-19
題目連結:http://codeforces.com/gym/101620/attachments
題目大意:給出一個包含 n 個點 m 條邊的無向圖。一個人一開始在編號為1的結點,現在他想前往編號為 n 的結點。在前往結點n 的過程中,他會以以下的方式選擇走的點,當他在結點 x 的時候,他會花費一枚硬幣等概率地選擇出一個與 x 相鄰的點,如果他選擇的點到達結點n所花費的金幣個數的期望不是最小的,他就會選擇重新花費一枚硬幣繼續選擇他將前往的點。現在要你求出他從結點1走到結點n的過程中所花費的硬幣的期望是多少。
題目思路:如果我們考慮直接從結點1開始去求到達結點n的期望的話,是無法求解出來的,因為對於每一步的情況都是有無限種的。所以我們可以考慮倒著做,從結點n往結點1倒推。
現在設 dp[x] 表示從結點 x 前往結點 n 所花費的硬幣數的期望,那麼很容易知道dp[n] = 0的。
接下來,對於結點 x 來說,由於他每次只會選擇期望最小的點走。
所以可以得到狀態轉移方程為:,v表示與x相鄰的點,cnt[x]表示所有可能的情況數。
這樣我們可以藉助優先佇列來維護上面的min(dp[x],dp[v]),往優先佇列中放入不同點的dp值,每次從優先佇列中取出dp值最小的點,更新周圍的點,類似於最短路的dijstra演算法的鬆弛過程。
具體實現看程式碼:
#include <bits/stdc++.h> #define fi first #define se second #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pb push_back #define MP make_pair #define lowbit(x) x&-x #define clr(a) memset(a,0,sizeof(a)) #define _INF(a) memset(a,0x3f,sizeof(a)) #define FIN freopen("in.txt","r",stdin) #define IOS ios::sync_with_stdio(false) #define fuck(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int>pii; typedef pair<double, int>P; const int MX = 3e5 + 7; int n, m; vector<int>G[MX]; double dp[MX], sum[MX]; int cnt[MX]; bool vis[MX]; void solve() { priority_queue<P, vector<P>, greater<P> >q; q.push(MP(0, n)); while (!q.empty()) { int u = q.top().se; q.pop(); if (vis[u]) continue; vis[u] = 1; for (auto v : G[u]) { if (vis[v]) continue; cnt[v]++; sum[v] += dp[u]; dp[v] = sum[v] / cnt[v]; q.push(MP(dp[v], v)); } } } int main() { // FIN; scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { int u, v; scanf("%d%d", &u, &v); G[u].pb(v); G[v].pb(u); } for (int i = 1; i < n; i++) sum[i] = G[i].size(); dp[n] = 0; solve(); printf("%.7f\n", dp[1]); return 0; }