洛谷P4316 绿豆蛙的归宿(概率DP/期望DP+拓扑排序)
题目背景
随着新版百度空间的上线,Blog 宠物绿豆蛙完成了它的使命,去寻找它新的归宿。
题目描述
给出张 nnn 个点 mmm 条边的有向无环图,起点为 111,终点为 nnn,每条边都有一个长度,并且从起点出发能够到达所有的点,所有的点也都能够到达终点。
绿豆蛙从起点出发,走向终点。 到达每一个顶点时,如果该节点有 kkk 条出边,绿豆蛙可以选择任意一条边离开该点,并且走向每条边的概率为 1k\frac{1}{k}k1 。现在绿豆蛙想知道,从起点走到终点的所经过的路径总长度期望是多少?
输入格式
输入的第一行是两个整数,分别代表图的点数 nnn 和边数 mmm。
第 222 到第 (m+1)(m + 1)(m+1) 行,每行有三个整数 u,v,wu, v, wu,v,w,代表存在一条从 uuu 指向 vvv 长度为 www 的有向边。
输出格式
输出一行一个实数代表答案,四舍五入保留两位小数。
输入输出样例
输入 #1
4 4 1 2 1 1 3 2 2 3 3 3 4 4
输出 #1
7.00
和之前AtCoder那个题很类似,只不过那个题已经是点序号由大到小按照拓扑序排了,所以直接从大到小遍历即可,而这个题必须得边排序边dp。dp[i]代表从i到n路径长度的期望,则转移方程:dp[i]=(∑dp[k]+edge[i,k])/degree[i],其中k为i出边的终点,edge[i,k]为i到k边的长度,degree[i]为i的出度。从终点到起点递推需要建立反图,同时两个数组存储出度,out数组用作转移方程用,out1数组用作拓扑排序用(貌似不用两个数组QuQ)。
#include <bits/stdc++.h> #define N 100005 #define M 200005 using namespace std; int n,m,head[N],ver[M],edge[M],Next[M],tot=0; double dp[N]; int out[N]={0},out1[N]={0};//out[i]代表i这个点的出度(原图的,并非反图的) void add(int x,int y,int z) { ver[++tot]=y;edge[tot]=z; Next[tot]=head[x],head[x]=tot; } void process() { queue<int>q; q.push(n); dp[n]=0; while(q.size())//拓扑排序 { int pre=q.front(); q.pop(); int i; for(i=head[pre];i;i=Next[i]) { int y=ver[i],z=edge[i]; dp[y]+=(dp[pre]+z)*1.0/out[y]; if(--out1[y]==0)//拓扑排序条件 { q.push(y); } } } } int main() { cin>>n>>m; int i; for(i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); add(y,x,z);//逆推 建反图 out[x]++; out1[x]++; } process(); printf("%.2lf",dp[1]); return 0; }