DestinHistoire

 

BZOJ-3036 绿豆蛙的归宿(拓扑排序+概率dp)

题目描述

  给一个有向无环的连通图,\(n(1\leq n\leq 10^5)\) 个点 \(m(1\leq m\leq 2n)\) 条边,起点为 \(1\) 终点为 \(n\),每条边都有一个长度。绿豆蛙从起点出发,走向终点。到达每一个顶点时,如果有 \(k\) 条离开该点的道路,绿豆蛙可以选择任意一条道路离开该点,并且走向每条路的概率为 \(\frac{1}{k}\) 。求从起点走到终点的所经过的路径总长度期望。

分析

  设 \(dp[x]\) 表示从节点 \(x\) 走到终点所经过的路径的期望长度。若从 \(x\) 出发有 \(k\) 条边,分别到达 \(y_1,y_2,\cdots,y_k\),边长分别为 \(z_1,z_2,\cdots,z_k\) ,则根据数学期望的定义和性质,有:

\[dp[x]=\frac{1}{k}\sum\limits_{i=1}^{k}(dp[y_i]+z_i) \]

  边界条件为 \(dp[n]=0\)

  从终点出发,在反图上(每条边方向取反的图上)执行拓扑排序,在拓扑排序的过程中顺便计算 \(dp[x]\),时间复杂度 \(O(n+m)\)

代码

#include<bits/stdc++.h>
using namespace std;
struct Edge
{
	int to;
	int Next;
	int dis;
}edge[200010];
int head[200010],num_edge;
void add_edge(int from,int to,int dis)
{
	edge[++num_edge].to=to;
	edge[num_edge].dis=dis;
	edge[num_edge].Next=head[from];
	head[from]=num_edge;
}
int deg[100010],out[100010];
double dp[100010];
queue<int> Q;
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add_edge(y,x,z);
		deg[x]++;
		out[x]++;
	}
	Q.push(n);
	while(!Q.empty())
	{
		int x=Q.front();
		Q.pop();
		for(int i=head[x];i;i=edge[i].Next)
		{
			int y=edge[i].to;
			dp[y]=dp[y]+(dp[x]+edge[i].dis)/deg[y];
			out[y]--;
			if(out[y]==0)
				Q.push(y);
		}
	}
	printf("%.2lf\n",dp[1]);
	return 0;
}

posted on 2020-12-07 15:06  DestinHistoire  阅读(59)  评论(0编辑  收藏  举报

导航