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) 编辑 收藏 举报