• UVA11090 Going in Cycle!!


    传送门

    经典题

    如果把每个环都找一遍绝对时间爆炸

    所以我们要换一种思路

    看到求最大最小首先考虑二分答案

    如果平均权值最小的回路小于我们二分的答案mid会发生什么呢

    如果我们把回路的长度减少 mid*回路边数,回路的长度就会变成负数

    而把回路减少 mid*边数 其实相当于把回路上的每条边都减少mid

    减完后图中就出现了负环,用spfa可以判负环

    所以复杂度就是 o(log($len_{max}-len_{min}$) * 玄学)...

    注意图的联通性,每个联通块都要判一波负环

    别忘了可能图没环

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=5007;
    const double eps=1e-4;//精度要求不高
    int n,m;
    int fir[N],from[N<<1],to[N<<1],val[N<<1],cntt;
    inline void add(int a,int b,int c)
    {
        from[++cntt]=fir[a];
        fir[a]=cntt; to[cntt]=b; val[cntt]=c;
    }
    inline bool pd(double x,double y) { return x-y>eps ? 1 : 0; }//判断x是否大于y
    double dis[N];
    int cnt[N];
    bool vis[N],P[N];
    inline bool spfa(int st,double mid)//spfa判负环
    {
        queue <int> q;
        memset(cnt,0,sizeof(cnt));
        memset(dis,127,sizeof(dis));
        q.push(st); vis[st]=1; dis[st]=0.0;
        while(!q.empty())
        {
            int x=q.front(); q.pop(); vis[x]=0; P[x]=1;
            for(int i=fir[x];i;i=from[i])
            {
                int &v=to[i];
                if( pd(dis[v],dis[x]+val[i]-mid) )
                {
                    dis[v]=dis[x]+val[i]-mid;
                    cnt[v]=cnt[x]+1; if(cnt[v]>=n) return 1;//如果有负环返回1
                    if(!vis[v]) { q.push(v); vis[v]=1; }
                }
            }
        }
        return 0;
    }
    inline bool check(double mid)//判合法性
    {
        bool flag=0;
        memset(P,0,sizeof(P));
        for(int i=1;i<=n&&!flag;i++) flag|=spfa(i,mid);//每个联通块都要判
        return !flag;
    }
    int T;
    int main()
    {
        T=read();
        for(int j=1;j<=T;j++)//多组数据
        {
            memset(fir,0,sizeof(fir)); cntt=0;
            memset(from,0,sizeof(from)); memset(val,0,sizeof(val));
            int a,b,c;
            double l=1e8,r=-1e8,mid;//l,r是最短边和最长边
            n=read(); m=read();
            for(int i=1;i<=m;i++)
            {
                a=read(); b=read(); c=read();
                add(a,b,c);
                l=min(l,(double)c); r=max(r,(double)c);
            }
            if(check(r+1)) { printf("Case #%d: No cycle found.
    ",j); continue; }//判断是否有环
            while(pd(r,l))//二分答案
            {
                mid=(l+r)/2;
                if(check(mid)) l=mid;
                else r=mid;
            }
            printf("Case #%d: %.2lf
    ",j,l);
        }
        return 0;
    }

      其实这算是个简单的$01$规划问题了

  • 相关阅读:
    C++类的成员函数的指针和mem_fun适配器的用法
    C++ RTTI的使用
    C++特殊工具与技术之RTTI
    Linux组件封装之五:生产者消费者问题
    Linux组件封装之四:RAII实现MutexLock自动化解锁
    Linux组件封装之三:Thread
    Linux组件封装之二:Condition
    Linux组件封装之一:MUtexLock
    C++ socket与Flex as3通信的沙盒问题解决
    CentOS搭建PHP环境
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9842762.html
Copyright © 2020-2023  润新知