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]\),
我们显然可以列出方程
很容易看出来这个方程是倒推转移的,然后我们反向建图,拓扑序\(dp\)即可。
其实正推也是可以做的,只是比较麻烦,并不能采取上述的\(dp\)思路。
假设采取上面的转移方程,那么就应该写成这样:
如果这样做的话,我们会将之前所有的情况全部加上一个\(w[x->y]\)。
假设\(g[x]\)为\(1到x的概率\),即认定\(g[x]==1\),显然是不成立的。
正确的方程式应为
而倒推中\(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]);
}