zoukankan      html  css  js  c++  java
  • 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 imes 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-{1over k}}) imes f[to] + {1over k} imes (e[i].w + f[to]))

    解释一下他会有 (1over k) 的概率走这条边,并且走这条边增加的路径长度是 (e[i].w),期望为 ({1over k} imes e[i].w)

    再加上到 (to) 的期望长度就是走这条边的期望,有 (1-{1over k}) 的概率不走这条边,路径长度保持不变,即 (f[to]) 就是不走这条边的期望。

    合并一下就可以变成 (f[x] = displaystyle sum_{to in to[x]}f[to] + {1over k} imes 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;
    }
    
  • 相关阅读:
    代码管理模型概况
    循环链表
    队列

    链表
    java 2020-10-12T11:22:49.000+0800 字符串转换成正常时间格式
    动态数组
    mysql练习
    复杂度与LeetCode
    记一次带逗号的数字类型处理
  • 原文地址:https://www.cnblogs.com/genshy/p/13631569.html
Copyright © 2011-2022 走看看