题目链接: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=deg∑ey+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 deg∑min(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(degv−1)∗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);
}