P4316 绿豆蛙的归宿

P4316 绿豆蛙的归宿

题目背景

随着新版百度空间的上线,Blog 宠物绿豆蛙完成了它的使命,去寻找它新的归宿。

题目描述

给出张 \(n\) 个点 \(m\) 条边的有向无环图,起点为 \(1\),终点为 \(n\),每条边都有一个长度,并且从起点出发能够到达所有的点,所有的点也都能够到达终点。

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

输入格式

输入的第一行是两个整数,分别代表图的点数 \(n\) 和边数 \(m\)

\(2\) 到第 \((m+1)\) 行,每行有三个整数 \(u, v, w\),代表存在一条从 \(u\) 指向 \(v\) 长度为 \(w\) 的有向边。

输出格式

输出一行一个实数代表答案,四舍五入保留两位小数。

输入输出样例

输入 #1

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

输出 #1

7.00

说明/提示

数据规模与约定

  • 对于 20% 的数据,保证 \(n \leq 10^2\)

  • 对于 40% 的数据,保证 \(n \leq 10^3\)

  • 对于 60% 的数据,保证 \(n \leq 10^4\)

  • 对于 100% 的数据,保证 \(1 \leq n \leq 10^5\)\(1 \leq m \leq 2 \times n\)\(1 \leq u, v \leq n\)\(1 \leq w \leq 10^9\),给出的图无重边和自环。

比较基础概率\(dp\)

\(f[i]\) 表示从 \(i\) 走到 \(n\) 的期望长度。

就有 \(f[x] = \displaystyle \sum_{to \in to[x]} ({1-{1\over k}}) \times f[to] + {1\over k} \times (e[i].w + f[to])\)

解释一下他会有 \(1\over k\) 的概率走这条边,并且走这条边增加的路径长度是 \(e[i].w\),期望为 \({1\over k} \times e[i].w\)

再加上到 \(to\) 的期望长度就是走这条边的期望,有 \(1-{1\over k}\) 的概率不走这条边,路径长度保持不变,即 \(f[to]\) 就是不走这条边的期望。

合并一下就可以变成 \(f[x] = \displaystyle \sum_{to \in to[x]}f[to] + {1\over k} \times e[i].w\)

因此,根据期望的定义可以得到上面的那个转移方程。

期望一般都是倒着推的,所以我们要建反图。\(k\) 的话就是每个点的入度。

建反图跑拓扑序就能得到最后的答案。

Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
int n,m,u,v,w,tot;
int head[100010],du[100010],d[100010];
double f[100010];
queue<int> q;
struct node
{
	int to,net,w;
}e[200010];
void add(int x,int y,int w)
{
	e[++tot].w = w;
	e[tot].to = y;
	e[tot].net = head[x];
	head[x] = tot;
}
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
	return s * w;
}
int main()
{
	n = read(); m = read();
	for(int i = 1; i <= m; i++)
	{
		u = read(); v = read(); w = read();//建反向图
		add(v,u,w); du[u]++;//纪录每个点在反图上的入度
	}
	for(int i = 1; i <= n; i++) d[i] = du[i];
 	f[n] = 0.0; q.push(n);//跑拓扑序
	while(!q.empty())
	{
		int t = q.front(); q.pop();
		for(int i = head[t]; i; i = e[i].net)
		{
			int to = e[i].to;
			f[to] += 1.0 * (1.0 / d[to]) * (f[t] + e[i].w);//递推
			if(--du[to] == 0)
			{
				q.push(to);
			}
		}
	}
	printf("%.2lf",f[1]);
	return 0;
}
posted @ 2020-09-08 11:45  genshy  阅读(218)  评论(0编辑  收藏  举报