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
",f[1]);
}
原文地址:https://www.cnblogs.com/lcxer/p/9838729.html