Label
DAG转化成拓扑序后逆向DP
Description
给定一张 n n n个点 m m m条边、起点为 1 1 1终点为 n n n的DAG,每条边均有一个长度,且从起点出发可到达所有的点,所有的点也能到达终点。
绿豆蛙从起点出发走向终点,到达每一个顶点时,若有 k k k条离开该点的道路,它会等概率地选择任意一条道路离开该点,即走向每条路的概率均为 1 k \frac{1}{k} k1。求它从起点到终点所经过地路径的期望长度为多少。
数据范围: 1 ≤ n ≤ 1 0 5 , 1 ≤ m ≤ 2 n 1\leq n \leq 10^5,1\leq m \leq 2n 1≤n≤105,1≤m≤2n,答案四舍五入后保留两位小数输出。
Solution
(友情提示:本文中(u,v,w)代表从点u通向点v的,边权为w的有向边)
顺推—一个不好搞的思路
用 d p [ i ] dp[i] dp[i]表示从点 1 1 1到点 i i i所经过的路径长度的期望,那么:
d p [ i ] = ∑ p k ( d p [ u [ k ] + w [ k ] ] ) dp[i]=\sum p_k(dp[u[k]+w[k]]) dp[i]=∑pk(dp[u[k]+w[k]]),其中 p k p_k pk为走编号为k的边的概率,但问题来了:原题意中说的是“等概率地走离开某点的所有道路”,所以所有到达某点的道路走的概率着实不好算(有一种经典的错误思路为 d p [ i ] = ∑ 1 o u t u [ k ] ( d p [ u [ k ] ] + w [ k ] ) dp[i]=\sum\frac{1}{ out_{u[k]}}(dp[u[k]]+w[k]) dp[i]=∑outu[k]1(dp[u[k]]+w[k])( o u t [ u [ k ] ] out[u[k]] out[u[k]]为点 u [ k ] u[k] u[k]的出度),但 ∑ 1 o u t u [ k ] \sum\frac{1}{ out_{u[k]}} ∑outu[k]1显然不等于1,此式不符合期望性质);故考虑转换思路。
逆推
在期望题中,逆推的使用条件往往是**“终止状态确定时可用逆推”,那么我们考虑想一个终止状态确定的状态**。
此处状态与顺推相反,
d
p
[
i
]
dp[i]
dp[i]不再表示从点
1
1
1到点
i
i
i所经过的路径长度的期望,而是从点
i
i
i到点
n
n
n所经过的路径长度的期望。知道了状态,我们很容易列出符合期望公式
E
(
x
)
=
∑
p
i
x
i
E(x)=\sum p_i x_i
E(x)=∑pixi的
d
p
dp
dp式:
d
p
[
x
]
=
∑
i
=
1
o
u
t
[
x
]
1
o
u
t
[
x
]
(
d
p
[
v
[
i
]
]
+
w
[
i
]
)
dp[x]=\sum_{i=1}^{out[x]}\frac{1}{out[x]}(dp[v[i]]+w[i])
dp[x]=∑i=1out[x]out[x]1(dp[v[i]]+w[i])
(
d
p
[
n
]
=
0
)
(dp[n]=0)
(dp[n]=0)
其中
o
u
t
[
x
]
out[x]
out[x]为点
x
x
x的出度。
由于是逆推,故可以在拓扑排序过程中实现答案统计。
Code
#include<cstdio>
#include<iostream>
#define ri register int
using namespace std;
const int MAXN=200020;
int n,m,top,u[MAXN],v[MAXN],fst[MAXN],nxt[MAXN],out[MAXN],dfn[MAXN];
double w[MAXN],dp[MAXN];
void topsort(int x)
{
dfn[x]=1;
for(ri k=fst[x];k>0;k=nxt[k])
{
if(dfn[v[k]])//此处if顺序不能写反!
{
dp[x]+=dp[v[k]]+w[k];
continue;
}
topsort(v[k]);
dp[x]+=dp[v[k]]+w[k];//统计出度点的答案
}
if(out[x]!=0) dp[x]/=out[x];
}
int main()
{
scanf("%d%d",&n,&m);
top=n;
for(ri i=1;i<=m;i++)
{
scanf("%d%d%lf",&u[i],&v[i],&w[i]);
nxt[i]=fst[u[i]],fst[u[i]]=i;
++out[u[i]];
}
topsort(1);
printf("%.2f",dp[1]);//.xf自带四舍五入功能
return 0;
}