LOJ #10084. 「一本通 3.3 练习 1」最小圈(二分+SPFA判负环)

题意描述:

   见原LOJ:https://loj.ac/problem/10084

 

题解:

  假设所求的平均最小值为X,环上各个边的权值分别为A1,A2...Ak,可以得到:

    X=(A1+A2+A3+...+Ak)/K,

    A1+A2+A3+...+Ak=X*K,

    移项可得:(A1-X)+(A2-X)+(A3-X)+...+(Ak-X)=0,

    另外注意到式子中的等于号可以改为大于等于,那么我们可以二分结果ans,然后判断是否存在一组解满足(A1+A2+A3+...+Ak)/k<=ans,

    即判断:(A1-ans)+(A2-ans)+(A3-ans)+...+(Ak-ans)<=0;

    最后问题就变成了二分一个ans后在图中判断是否存在一个负环。

 

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 30009
#define maxm  10009
#define eps 1e-9
inline ll read()
{
    ll 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)+(ll)(ch-'0');ch=getchar();}
    return x*f;
}
bool flag;
bool vis[maxn];
double dis[maxn];
int head[maxn];
struct edge
{
    int to,nxt;
    double val;
}p[maxm];
int n,m,k,tot,cnt;
double ans,sum,l,r; 

void add(int x,int y,double z)
{
    ++cnt,p[cnt].to=y,p[cnt].nxt=head[x],p[cnt].val=z,head[x]=cnt;
}

void  Spfa(int u,double ave)
{
    if(flag)
        return ;
    vis[u]=1;
    for(int i=head[u];i;i=p[i].nxt)
    {
        int v=p[i].to;
        if(dis[u]+p[i].val-ave<dis[v])
        {
            dis[v]=dis[u]+p[i].val-ave;
            if(!vis[v])
                Spfa(v,ave);
            if(flag)
                return ;
            else if(vis[v])
            {
                flag=true;
                return ;
            }
        }
    }    
    vis[u]=0;
}
bool Check(double ave)
{
    flag=false;
    memset(vis,0,sizeof(vis));
    for(int j=0;j<maxn;j++)
        dis[j]=0;
    for(int i=1;i<=n;i++)
    {
        Spfa(i,ave);
        if(flag)
            break;
    }
    return flag;
}
int main()
{
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        double z;scanf("%lf",&z);
        add(x,y,z);
    }
    l=-1e7,r=1e7;
    while((r-l)>eps)
    {
        double mid=(l+r)/2;
        if(Check(mid))
        {
            ans=mid;
            r=mid;
        }
        else
            l=mid;
    }
    printf("%0.8lf",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

 

posted @ 2018-10-13 11:04  月下的魔术师0310  阅读(204)  评论(0编辑  收藏  举报