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.\n",j); continue; }//判断是否有环
        while(pd(r,l))//二分答案
        {
            mid=(l+r)/2;
            if(check(mid)) l=mid;
            else r=mid;
        }
        printf("Case #%d: %.2lf\n",j,l);
    }
    return 0;
}

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

posted @ 2018-10-24 11:57  LLTYYC  阅读(231)  评论(0编辑  收藏  举报