HNOI2009 最小圈

题目链接:戳我

发现了判断负环的真*写法喂所以说原先那个不是标准判断方法吗!!

就是一个简单的01分数规划,然后我们知道\(\sum w \ge k*mid\)

然后这就相当于把所有的边都减去二分出来的mid值,如果有负环就说明这个二分值大于等于答案了。

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<ctime>
#define MAXN 100010
#define eps 1e-9
using namespace std;
int n,m,t;
int head[MAXN],done[MAXN],cnt[MAXN];
double dis[MAXN];
struct Edge{int nxt,to;double dis;}edge[MAXN<<1];
inline void add(int from,int to,double dis)
{
    edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis;
    head[from]=t;
}
inline bool spfa(int x)
{
    done[x]=1;
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(dis[v]>dis[x]+edge[i].dis)
        {
            dis[v]=dis[x]+edge[i].dis;
            if(done[v]||spfa(v)) return true;
        }
    }
    done[x]=0;
    return  false;
}
inline bool check(double x)
{
    for(int i=1;i<=n;i++)
        for(int j=head[i];j;j=edge[j].nxt)
            edge[j].dis-=x;
    for(int i=1;i<=n;i++) dis[i]=0,done[i]=0;
    bool flag=false;
    for(int i=1;i<=n;i++)
        if(spfa(i))
        {
            flag=true;
            break;
        }
    for(int i=1;i<=n;i++)
        for(int j=head[i];j;j=edge[j].nxt)
            edge[j].dis+=x;
    return flag;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d%d",&n,&m);
    double l=1e9,r=-1e9;
    for(int i=1;i<=m;i++)
    {
        int x,y;double w;
        scanf("%d%d%lf",&x,&y,&w);
        add(x,y,w);
        l=min(1.0*w,l),r=max(r,1.0*w);
    }
    //cout<<l<<" "<<r<<endl;
    while(l+eps<r)
    {
        double mid=(l+r)/2;
        //	printf("[%.8lf %.8lf] mid=%.8lf\n",l,r,mid);
        if(check(mid)) r=mid;
        else l=mid;
    }
    printf("%.8lf\n",l);
    return 0;
}


posted @ 2019-05-21 08:48  风浔凌  阅读(99)  评论(0编辑  收藏  举报