Loading

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

 

posted @ 2020-03-22 20:38  脂环  阅读(201)  评论(0编辑  收藏  举报