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 
#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)<<"]"<pii;
typedef pairP;
const int MX = 3e5 + 7;

int n, m;
vectorG[MX];
double dp[MX], sum[MX];
int cnt[MX];
bool vis[MX];

void solve() {
    priority_queue, greater

>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; }

 

你可能感兴趣的:(ACM,dp)