【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
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值