1. 程式人生 > >2017-2018 ACM-ICPC, Central Europe Regional Contest (CERC 17) G - Gambling Guide(期望dp)

2017-2018 ACM-ICPC, Central Europe Regional Contest (CERC 17) G - Gambling Guide(期望dp)

題目連結: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 來說,由於他每次只會選擇期望最小的點走。

所以可以得到狀態轉移方程為:dp[x] = \frac{\sum min(dp[x],dp[v]) }{cnt[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;
}