2017-2018 ACM-ICPC, Central Europe Regional Contest (CERC 17):G. Gambling Guide(最短路dp)

題目鏈接:https://codeforces.com/gym/101620

題意:你需要乘車從點11去點nn,你在每個點花費一塊錢可以買車票,售票機會隨機給你吐出一個相鄰車站的車票,你可選擇坐車走也可以選擇丟棄這張票再繼續買。你知道這個圖的形狀,那麼你預測你最少花費多少錢可以到達nn點。

解題心得:

  • dpdp真的好難啊,就這個題來說設dp[i]dp[i]爲從ii點到達nn點預計花費的最少錢,那麼在這個狀態下dp[n]dp[n]爲已知狀態00,期望dpdp從已知到未知進行轉移。當我到達未知點xx的時候如果xx的度爲degdeg且周圍都是已知點,設xx點的到達nn點的期望爲exex,這個時候轉移方程式爲ex=eydeg+1ex = \frac {\sum ey} {deg} + 1,對於這個公式的理解可以看作我從當前點隨機走到已知點再從已知點到nn點的最小期望。
  • 但是上面公式是xx點周圍的已知點都已經得到,其實更進一步可以想到min(ey,ex)deg+1\frac {\sum min(ey,ex)} {deg} + 1,也就說我即使得到了一個xx點不是最小期望exex,我也可以從周圍的點來進行更新得到更小的花費,這個更新花費的思路其實就是一個dijdij
  • 根據以上公式當有eu<eveu<ev時可以更新evev,此時ev=(degv1)ev+eudegv+1ev=\frac {(deg_{v}-1)*ev+eu} {deg_{v}} + 1,這個公式可以看作我買了一張票這個張票經過eueu的時候可以得到更小的答案,然後計算剛好經過eueu的概率,這個時候對於這個公式化簡得到ev=eu+degvev=eu+deg_{v},此後若又有ep<evep<ev,可以繼續更新ev=eu+ep+degv2ev=\frac {eu+ep+deg_{v}} {2},然後依次類推。


#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+100;
const double INF = 1e9;

struct Node {
    int useCnt, degree;
    double sum, p;
}node[maxn];

int n, m;
vector <int> ve[maxn];

void init() {
    scanf("%d%d", &n, &m);
    for(int i=1;i<=m;i++) {
        int a, b; scanf("%d%d",&a, &b);
        ve[a].push_back(b);
        ve[b].push_back(a);
        node[a].p = INF;
        node[b].p = INF;
        node[a].degree++;
        node[b].degree++;
    }
}

void dij() {
    node[n].useCnt = 1;
    node[n].p = 0;
    priority_queue <pair<double, int>, vector<pair<double, int>>, greater<pair<double, int>> > qu;
    qu.push(make_pair(0.0, n));

    while(!qu.empty()) {
        pair<double, int> now = qu.top();
        qu.pop();

        int u = now.second;
        double va = now.first;
        if(va > node[u].p) continue;
        for(int i=0;i<ve[u].size();i++) {
            int v = ve[u][i];
            if(node[v].useCnt == 0) {
                node[v].sum = node[u].p;
                node[v].p = node[u].p + node[v].degree;
                node[v].useCnt = 1;
                qu.push(make_pair(node[v].p, v));
            } else if(va < node[v].p) {
                node[v].useCnt ++;
                node[v].sum += node[u].p;
                node[v].p = (node[v].sum + node[v].degree) / node[v].useCnt;
                qu.push(make_pair(node[v].p, v));
            }
        }
    }
}

int main() {
    //    freopen("1.in.txt", "r", stdin);
    init();
    dij();
    printf("%.9f\n", node[1].p);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章