1. 程式人生 > >[BZOJ3036]綠豆蛙的歸宿:DAG上DP+期望DP

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