【bzoj1486】最小圈 分数规划


分数规划。

二分答案,用输入的权值减去答案。判负环。

需要用dfs判负环,spfa会T。

dfs判负环:
初始dist都为0。只走能更新dist的点。若走到了vis=1的点,则有负环。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int size=1000010;
const double INF=233333333.0;
int head[size],nxt[size],tot=0;
double dist[size];

struct edge{
    int t;
    double d,dd;
}l[size];

void build(int f,int t,double d)
{
    l[++tot].t=t;
    l[tot].dd=d;
    nxt[tot]=head[f];
    head[f]=tot;
}

int n,m,s;

bool vis[size];
bool dfs(int u)
{
    for(int i=head[u];i;i=nxt[i])
    {
        int v=l[i].t;
        if(dist[v]>dist[u]+l[i].d)
        {
            dist[v]=dist[u]+l[i].d;
            if(vis[v]) return true;
            vis[v]=1;
            if(dfs(v)) return true;
            vis[v]=0;
        }
    }
    return false;
}

bool check(double ans)
{
    for(int i=1;i<=tot;i++) l[i].d=l[i].dd-ans;
// for(int i=1;i<=tot;i++) cout<<l[i].t<<" "<<l[i].d<<" ";puts("");
    memset(dist,0,sizeof(dist));
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)
    {
        if(!vis[i]&&dfs(i)) return true;
    }
    return false;   
}


double div()
{
    double l=-INF,r=INF;
    for(int i=1;i<=60;i++)
    {
        double mid=(l+r)/2.0;
        if(check(mid)) r=mid;
        else l=mid;
// for(int j=1;j<=n;j++) cout<<dist[j]<<" ";puts(""); cout<<mid<<endl;
    }
    return l;
}

void scanf(int &n)
{
    n=0;
    int flag=1;
    char a=getchar();
    while(a>'9'||a<'0') {if(a=='-') flag=-1;a=getchar();}
    while('0'<=a&&a<='9') 
    {
        n=(n<<3)+(n<<1)+a-'0';
        a=getchar();
    }
    n*=flag;
}

int main()
{
    scanf(n);   scanf(m);
    for(int i=1;i<=m;i++)
    {
        int a,b;double c;
        scanf(a);   scanf(b);
        scanf("%lf",&c);
        build(a,b,c);
    }
    printf("%.8lf\n",div());
    return 0;
}
/* 4 5 1 2 5 2 3 5 3 1 5 2 4 3 4 1 3 2 2 1 2 -2.9 2 1 -3.1 */

你可能感兴趣的:(bzoj)