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;
}