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

题目链接:https://codeforces.com/gym/101620

题意:你需要乘车从点 1 1 1去点 n n n,你在每个点花费一块钱可以买车票,售票机会随机给你吐出一个相邻车站的车票,你可选择坐车走也可以选择丢弃这张票再继续买。你知道这个图的形状,那么你预测你最少花费多少钱可以到达 n n n点。

解题心得:

  • d p dp dp真的好难啊,就这个题来说设 d p [ i ] dp[i] dp[i]为从 i i i点到达 n n n点预计花费的最少钱,那么在这个状态下 d p [ n ] dp[n] dp[n]为已知状态 0 0 0,期望 d p dp dp从已知到未知进行转移。当我到达未知点 x x x的时候如果 x x x的度为 d e g deg deg且周围都是已知点,设 x x x点的到达 n n n点的期望为 e x ex ex,这个时候转移方程式为 e x = ∑ e y d e g + 1 ex = \frac {\sum ey} {deg} + 1 ex=degey+1,对于这个公式的理解可以看作我从当前点随机走到已知点再从已知点到 n n n点的最小期望。
  • 但是上面公式是 x x x点周围的已知点都已经得到,其实更进一步可以想到 ∑ m i n ( e y , e x ) d e g + 1 \frac {\sum min(ey,ex)} {deg} + 1 degmin(ey,ex)+1,也就说我即使得到了一个 x x x点不是最小期望 e x ex ex,我也可以从周围的点来进行更新得到更小的花费,这个更新花费的思路其实就是一个 d i j dij dij
  • 根据以上公式当有 e u < e v eu<ev eu<ev时可以更新 e v ev ev,此时 e v = ( d e g v − 1 ) ∗ e v + e u d e g v + 1 ev=\frac {(deg_{v}-1)*ev+eu} {deg_{v}} + 1 ev=degv(degv1)ev+eu+1,这个公式可以看作我买了一张票这个张票经过 e u eu eu的时候可以得到更小的答案,然后计算刚好经过 e u eu eu的概率,这个时候对于这个公式化简得到 e v = e u + d e g v ev=eu+deg_{v} ev=eu+degv,此后若又有 e p < e v ep<ev ep<ev,可以继续更新 e v = e u + e p + d e g v 2 ev=\frac {eu+ep+deg_{v}} {2} ev=2eu+ep+degv,然后依次类推。


#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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值