CERC2017 Gambling Guide,最短路变形,期望dp

题目链接

题面链接

题意

给定一个无向图,你需要从 1 1 点出发到达n点,你在每一点的时候,使用 1 1 个单位的代价,随机得到相邻点的票,但是你可以选择留在原地,也可以选择使用掉这张票,问到达n点的最小代价的方案的期望是多少。

题解

我们先假定在最优方案下从每个点 x x 出发,到达n点的代价的期望为 ex e x ,那么显然,我们可以列出方程 ex=min(ex,ey)degx+1 e x = ∑ m i n ( e x , e y ) d e g x + 1 ,其中 y y x节点相连。初值 dpn=0 d p n = 0

借鉴 GXZlegend G X Z l e g e n d 的一句话,遇见“初始值只有一个点的 dp d p 值确定,其他点的 dp d p 值依赖于已经计算出来的点的 dp d p 值”这种类型的题,往往考虑使用最短路的方式转移。

观察方程,如果我们按照计算出来的 ex e x 从小到大的方式遍历的话,那么先计算出来的 ex e x 一定不会再被后计算出来的值更新,满足跟最短路一样的性质。

我们一开始假定所有的 ex|x!=n=inf e x | x ! = n = i n f ,并且 en=0 e n = 0 ,每个点的 min(ex,ey) m i n ( e x , e y ) 都取 ex e x

那么我们模拟一下这个转移过程,当前从堆里取出的点是 u u ,相邻的点有v,我们发现 v v 是第一次被更新,因为ev=inf,满足 ev>eu e v > e u ,那么 v v 就要被更新,即根据方程ev=(degv1)ev+eudegv+1,那么 ev=eu+degv e v = e u + d e g v
如果接下来某次取出的点是 p p ,相邻的点还有v,并且 ev>ep e v > e p ,那么 ev e v 就要被二次更新了,也就是 ev=(degv2)ev+eu+epdegv+1 e v = ( d e g v − 2 ) e v + e u + e p d e g v + 1 ,那么 ev=eu+ep+degv2 e v = e u + e p + d e g v 2 。依次类推。

代码

#include <iostream>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 300007;
const double inf = 1e9;
int n,m;
vector<int> G[maxn];
typedef pair<double,int> pii;
priority_queue<pii,vector<pii>,greater<pii> > Q;
double dp[maxn];
int deg[maxn],vis[maxn];
int usedeg[maxn];
void dij(){
    for(int i = 1;i < n;++i) dp[i] = inf;
    Q.push({0,n});
    vis[n] = 1;
    while(!Q.empty()){
        pii p = Q.top();Q.pop();
        int u = p.second;
        double nowdp = p.first;
        if(nowdp > dp[u]) continue;
        for(int v:G[u]){
            if(!vis[v]){
                vis[v] = 1;
                usedeg[v] = 1;
                dp[v] = nowdp+deg[v];
                Q.push({dp[v],v});
            }
            else if(nowdp < dp[v]){
                double c = dp[v]*usedeg[v]-deg[v];
                usedeg[v]++;
                dp[v] = (c+nowdp+deg[v])/usedeg[v];
                Q.push({dp[v],v});
            }
        }
    }
}

int main(){
    cin>>n>>m;
    for(int i = 0;i < m;++i){
        int u,v;
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
        deg[u] ++,deg[v] ++;
    }
    dij();
    printf("%.12lf\n",dp[1]);

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值