The Preliminary Contest for ICPC Asia Nanjing 2019 ICPC南京站网络赛 D robots(随机游走概率DP+拓扑排序)

题目链接:https://nanti.jisuanke.com/t/41301

 

题目大意:有一张有向无环图,你要从入度为0的点走到一个出度为0的点,保证这两种点各只有一个,你可以走到下一个点也可以保持原地不动,每过一天可以走一步,每天都要加上一个值就是已过的天数作为代价,求代价的期望,比如到达一个点需要3天,那么就是1+2+3

 

题目思路:随机游走模板题?做过类似题目应该这题不成问题

首先先求出天数的期望

dp就是通过已知的信息来推出未知的信息,那么有什么东西的期望是我们已经知晓的呢?bingo,最后一个点他的天数期望一定是0(都已经到达终点了!)

所以这题就需要倒着来反向建图。

设dp[i]表示从i点到n的期望时间,那么他一共有可以去的点的数量+1种可能情况(跑路或者停留在原地),他去的点由于是倒着推,已经知道结果,所以就能得到当前的结果,当前的结果就是走一步到达j(j表示能到的点)或者在原地,然后再下一点到达n,所以公式为

dp[i]=\sum_{i->j} \frac{dp[j]+1}{x+1}+\frac{dp[i]+1}{x+1}

移项\frac{xdp[i]}{x+1}=\sum_{i->j} \frac{dp[j]+1}{x+1}+\frac{1}{x+1}

然后化简可以得到dp[i]=\frac{\sum_{i->j}dp[j]}{x}+\frac{x+1}{x}

但是这道题不是简简单单求个期望天数,而是求代价,其实就是每走一步都加上当前的期望天数

公式为

cost[i]=\sum_{i->j}\frac{cost[j]+dp[j]+1}{x+1}+\frac{cost[i]+dp[i]+1}{x+1}

然后按照刚才一样的方法化简以后,把dp[i]用刚才得到的式子代入再化简,就能得到

cost[i]=\sum_{i->j}\frac{cost[j]}{x}+\frac{dp[i]*(x+1)}{x}

 

以下是代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define per(i,a,b) for(ll i=a;i>=b;i--)
const int MAXN = 1e5+5;
int du[MAXN],du2[MAXN],du3[MAXN];
vector<int>g[MAXN];
double dp[MAXN],cost[MAXN];
queue<int>q;
int main(){
    int t,n,m,u,v;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        while(!q.empty())q.pop();
        memset(du,0,sizeof(du));
        memset(dp,0,sizeof(dp));
        memset(cost,0,sizeof(cost));
        rep(i,1,n)g[i].clear();
        rep(i,1,m){
            scanf("%d%d",&u,&v);
            g[v].push_back(u);
            du[u]++;
        }
        int temp=0,temp2=0;
        rep(i,1,n){
            if(!du[i])temp2=i,q.push(i);
            if(!g[i].size())temp=i;
            du3[i]=du2[i]=du[i];
        }
        while(!q.empty()){
            int u=q.front();
            q.pop();
            int len=g[u].size();
            if(du3[u]){
                dp[u]/=1.0*du3[u];
                dp[u]+=(1.0*du3[u]+1)/du3[u];
            }
            rep(i,0,len-1){
                int xx=g[u][i];
                dp[xx]+=dp[u];
                du[xx]--;
                if(!du[xx])q.push(xx);
            }
        }
        q.push(temp2);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            int len=g[u].size();
            if(du3[u]){
                cost[u]/=1.0*du3[u];
                cost[u]+=(1.0*du3[u]+1)*dp[u]/du3[u];
            }
            rep(i,0,len-1){
                int xx=g[u][i];
                cost[xx]+=cost[u];
                du2[xx]--;
                if(!du2[xx])q.push(xx);
            }
        }
        printf("%.2lf\n",cost[temp]);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值