https://atcoder.jp/contests/abc144/tasks/abc144_f
前提知識
解説
https://atcoder.jp/contests/abc144/submissions/8180528
グラフ全体はDAGになっているので、期待値DPをすることで答えが求まる。
まずは、通路を塞がないパターンを考えてみよう。
dp[cu] := 部屋curから部屋Nへ移動するのにかかる移動回数の期待値
dp[cu] = (dp[to1] + dp[to2] + ... + dp[toM]) / M + 1
これでdp[0]が答えになる。
これも見ると、大きい数から順番に確定させていけばよさそう。
とりあえず、通常DPをしよう。
さて、あとは塞がれる道だが、全探索するとO(M)となり、改めてDPするとO(M2)で間に合わない。
ある道を塞ぐと、s[i]への遷移がなくなる。
言い換えると、s[i]への遷移しか影響しない。
よって、ある頂点cuについてs[i]=cuを満たす道について考える。
この中で最適に道を消して最終的な期待値を最小化したいので、もっとも遷移先のdpの値が大きいものを消すのが最適。
これで、塞ぐべき道がO(M)からO(N)に減ったので、O(NM)となり間に合う。
int N, M; vector<int> E[601]; double dp[601], dp2[601]; //--------------------------------------------------------------------------------------------------- void _main() { cin >> N >> M; rep(i, 0, M) { int a, b; cin >> a >> b; a--; b--; E[a].push_back(b); } rrep(cu, N - 2, 0) { fore(to, E[cu]) dp[cu] += dp[to]; dp[cu] /= (int)E[cu].size(); dp[cu] += 1; } double ans = dp[0]; rep(stop, 0, N - 1) { rep(i, 0, N) dp2[i] = dp[i]; rep(i, 0, stop + 1) dp2[i] = 0; int sz = E[stop].size(); if (2 <= sz) { vector<double> v; fore(to, E[stop]) v.push_back(dp2[to]); sort(all(v)); rep(i, 0, sz - 1) dp2[stop] += v[i]; dp2[stop] /= sz - 1; dp2[stop] += 1; } else continue; rrep(cu, stop - 1, 0) { fore(to, E[cu]) dp2[cu] += dp2[to]; dp2[cu] /= E[cu].size(); dp2[cu] += 1; } chmin(ans, dp2[0]); } printf("%.10f\n", ans); }