[BZOJ3036]绿豆蛙的归宿:DAG上DP+期望DP

分析:

其实这道题是可以正着DP的(即不需要在反图上拓扑排序)(为什么好多人都说不能?)
我们令f[i]表示从起点1出发到点i的期望距离,如果一些点能到达点i,那么这些点中的每个点对f[i]的贡献为f[j]*po[j],po[j]表示通过j到达i的概率。实际上,我们令p[i]表示从起点1出发能到达i的概率,这个p[i]可以在拓扑排序同时求出,那么po[j]=(p[j]/out_deg[j])/p[i]。考虑到拓扑排序到j时我们还不知道p[i]的值,所以可以先给f[i]加上f[j]*(p[j]/out_deg[j]),在排到i时再给f[i]除以p[i]即可。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <queue>
typedef long long LL;

inline int read(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x;
}

const int MAXN=100005;
int n,m,ecnt,head[MAXN],in_deg[MAXN],out_deg[MAXN];
double p[MAXN],f[MAXN];
struct Edge{
    int to,nxt;
    LL w;
}e[MAXN<<1];
std::queue<int> q;

inline void add_edge(int bg,int ed,LL val){
    ecnt++;
    e[ecnt].to=ed;
    e[ecnt].nxt=head[bg];
    e[ecnt].w=val;
    head[bg]=ecnt;
}

int main(){
    n=read(),m=read();
    for(int i=1;i<=m;i++){
        int u=read(),v=read();
        LL w=read();
        add_edge(u,v,w);
        in_deg[v]++;
        out_deg[u]++;
    }
    while(!q.empty()) q.pop();
    q.push(1);
    f[1]=0,p[1]=1.0;
    while(!q.empty()){
        int x=q.front();q.pop();
        f[x]/=p[x];
        for(int i=head[x];i;i=e[i].nxt){
            int ver=e[i].to;
            p[ver]+=p[x]/out_deg[x];
            f[ver]+=(f[x]+e[i].w)*(p[x]/out_deg[x]);
            in_deg[ver]--;
            if(!in_deg[ver]) q.push(ver);
        }
    }
    printf("%.2lf\n",f[n]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ErkkiErkko/p/9807496.html