【NOIP2017提高组模拟6.27】B

14 篇文章 0 订阅

Description

泡泡鱼是一条调皮的鱼,ta的家住在一片珊瑚礁上。在ta的眼里,这些珊瑚礁的形态可以脑补成一个n个节点,m条边的带权图,在海水的腐蚀下,这些珊瑚礁形成了许多的环,ta想考考你能不能找出这些环中,权值的平均值最小的环。泡泡鱼这么聪明,ta当然知道答案,调皮的ta对你说,如果你算错了,就要吃ta下的蛋。因为ta很调皮,ta把图变成了有向图,还有可能用无环图坑你。为代表你知道,你只需告诉ta最小的平均权值即可。

Input

共m+1行。
第1行,2个整数n和m,表示珊瑚礁的点数和边数。
第2~m+1行,每行3个正整数u,v,w,表示u与v之间有一条权值为w的有向边。

Output

如果输入数据无环,输出”PaPaFish is laying egg!”。(不含引号)
否则输出一个浮点数ans, 表示所有环中,权值的平均值最小的环的平均权值。答案保留2位小数。

Sample Input

2 2
1 2 2
2 1 3

Sample Output

2.50

Data Constraint

对于前40%的数据n <= 50, m <= 5000
对于100%的数据1 <= n <= 1000, 1 <= m <= 10000, 0 <= w <= 10000000

Solution

二分答案,将边权减去答案,判负环

判负环有两种方法
一是直接用SPFA,当一个点的入队次数超过n次时就说明有负环
二是用dfs优化SPFA,标记数组记录点是否在栈中,当发现一个点的距离可以更新另一个点的距离,而被更新的点在栈中,则说明有负环
此题中第二种方法快

Code

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 1010
#define db double
using namespace std;
int last[N],next[N*100],to[N*100],tot=0,bz[N],n,m,flag;
db ans=21474354354321578,date[N*100],num[N],avg,f[N];
void putin(int x,int y,db z)
{
    next[++tot]=last[x];last[x]=tot;to[tot]=y;date[tot]=z;
}
void dg(int x)
{
    bz[x]=1;
    for(int i=last[x];i;i=next[i])
    {
        int y=to[i];
        if(flag) return;
        if(f[y]>f[x]+date[i]-avg)
        {
            f[y]=f[x]+date[i]-avg;
            if(bz[y]) flag=1;
            else dg(y);
        }
    }
    bz[x]=0;
}
bool pd(db m)
{
    memset(f,0,sizeof(f));
    memset(bz,0,sizeof(bz));
    flag=0;avg=m;
    fo(i,1,n)
    if(!bz[i])
    {
        dg(i);
        if(flag) return 1;
    }
    return 0;
}
int main()
{
    scanf("%d%d",&n,&m);
    fo(i,1,m)
    {
        int x,y;db z;
        scanf("%d %d %lf",&x,&y,&z);
        putin(x,y,z);
    }
    db l=0,r=10000000;
    while(l+0.00001<r)
    {
        db m=(l+r)/2;
        if(pd(m)) r=m;else l=m;
    }
    if(r>=9999999) printf("PaPaFish is laying egg!\n");
    else printf("%.2lf\n",l);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值