1. 程式人生 > >[HNOI2009]最小圈,洛谷P3199,分數規劃+判負環

[HNOI2009]最小圈,洛谷P3199,分數規劃+判負環

正題

      要你求一個環,使得邊權除以點數最小。

      就是求\frac{\sum_{i\in T w_i}}{\sum1}

      分數規劃,二分k,令這個東西小於k

      得到\sum_{i\in T}w_i-k<0

      判定問題成為一個判斷圖中是否有負環,新邊的邊權是原來的邊權減去k。

      判負環無高效演算法,dfs即可。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

int n,m;
struct edge{
    int x,y;
    double c;
}p[10010];
struct new_edge{
    int y,next;
    double c;
}s[10010];
int first[3010],len=0;
bool vis[3010],flag,tf[3010];
double dis[3010];

void ins(int x,int y,double c){
    len++;
    s[len]=(new_edge){y,first[x],c};first[x]=len;
}

void dfs(int x){
    for(int i=first[x];i!=0;i=s[i].next){
        int y=s[i].y;
        if(dis[y]>dis[x]+s[i].c){
            if(tf[y]){
                flag=true;
                return ;
            }
            else{
                dis[y]=dis[x]+s[i].c;
                tf[x]=true;
                dfs(y);
                tf[x]=false;
                if(flag) return ;
            }
        }
    }
}

bool check(double x){
    len=0;
    memset(first,0,sizeof(first));
    for(int i=1;i<=m;i++) ins(p[i].x,p[i].y,p[i].c-x);
    memset(dis,63,sizeof(dis));
    memset(vis,false,sizeof(vis));flag=false;
    memset(tf,false,sizeof(tf));
    for(int i=1;i<=n;i++)
        if(!vis[i] && !flag) dis[i]=0,dfs(i);
    return flag;
}

int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d %d %lf",&p[i].x,&p[i].y,&p[i].c);
    double l=-1e7,r=1e7;
    while(r-l>=1e-9){
        double mid=(l+r)/2;
        if(check(mid)) r=mid;
        else l=mid;
    }
    printf("%.8lf",l);
}