bzoj3036绿豆蛙的归宿

BZOJ3036绿豆蛙的归宿

Description

给出一个有向无环图,起点为\(1\)终点为\(N\),每条边都有一个长度,并且从起点出发能够到达所有的点,所有的点也都能够到达终点。绿豆蛙从起点出发,走向终点。
到达每一个顶点时,如果有K条离开该点的道路,绿豆蛙可以选择任意一条道路离开该点,并且走向每条路的概率为\(\frac{1}{K}\)
现在绿豆蛙想知道,从起点走到终点的所经过的路径总长度期望是多少?

Input

\(第一行: 两个整数 N M,代表图中有N个点、M条边\)
\(第二行到第 1+M 行: 每行3个整数a,b,c,代表从a到b有一条长度为c的有向边\)

Output

\(从起点到终点路径总长度的期望值,四舍五入保留两位小数。\)

Sample Input

4 4
1 2 1
1 3 2
2 3 3
3 4 4

Sample Output

7.00

HINT

\(对于20\)%\(的数据 N<=100\)

\(对于40\)%\(的数据 N<=1000\)

\(对于60\)%\(的数据 N<=10000\)

\(对于100\)%\(的数据 N<=100000,M<=2*N\)


\(题解:DAG上的期望dp。\)

这道题是良心题了,也是所选题中最水的一道,暂且不说暴力\(dfs\)能水过,就是正经做法也是最水的。

由于这是一个\(DAG\),所以我们可以设\(f[x]\)表示点\(x\)到终点\(n\)的期望路径总长。

显然可以看出答案为\(f[1]\).

现在我们来讨论怎么转移:

对于一条有向边\(x->y\),假设\(x\)的出度为\(deg[x]\),

我们显然可以列出方程

\[f[x]=\frac{1}{deg[x]}*\sum{(f[y]+w[x->y])} \]

很容易看出来这个方程是倒推转移的,然后我们反向建图,拓扑序\(dp\)即可。


其实正推也是可以做的,只是比较麻烦,并不能采取上述的\(dp\)思路。

假设采取上面的转移方程,那么就应该写成这样:

\[f[y]=\frac{1}{deg[x]}*\sum{(f[x]+w[x->y])} \]

如果这样做的话,我们会将之前所有的情况全部加上一个\(w[x->y]\)

假设\(g[x]\)\(1到x的概率\),即认定\(g[x]==1\),显然是不成立的。

正确的方程式应为

\[f[y]+=\frac{f[x]+g[x]*w[x->y]}{deg[x]} \]

而倒推中\(y只能到达n\),所以倒推的正确性显然。
以下给出倒推法代码:

#include<cstdio>
#include<queue>
int n,m,cnt,v[200001],pre[200001],nxt[200001],h[100001],in[100001],ot[100001];std::queue<int>q;double f[100001];
void add(int x,int y,int z){pre[++cnt]=y;nxt[cnt]=h[x];h[x]=cnt;v[cnt]=z;}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,x,y,z;i<=m;i++)scanf("%d%d%d",&x,&y,&z),add(y,x,z),in[x]++,ot[x]++;
    q.push(n);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=h[x];i;i=nxt[i])
        {
            f[pre[i]]+=(f[x]+v[i])/ot[pre[i]];
            if(!(--in[pre[i]]))q.push(pre[i]);
        }
    }
    printf("%.2lf\n",f[1]);
}
posted @ 2018-10-23 19:37  蒟蒻--lichenxi  阅读(145)  评论(0编辑  收藏  举报