[HNOI2009]最小圈 题解

题目大意

给你一个有向图,求出图中环的平均值的最小值

环的平均值定义:环中所有的边权和/环中点数量

思路

看到使平均值最大或最小,可以考虑分数规划

分数规划用于解决一些要让平均值最大或最小的问题

具体就是二分答案\(K\)

\(\frac{x_1+x_2+x_3+\dots+x_n}{n}\ge k\Leftrightarrow (x_1-k)+(x_2-k)+(x_3-k)+\dots+(x_n-k)\ge 0\)

很明显,这题完全满足这个分数规划的性质。

故我们枚举一个\(k\),把每条边的边权减去\(k\),再用\(SPFA\)判负环就可以了

具体细节见代码

#include <bits/stdc++.h>
using namespace std ;
const int MAXN = 10000 + 5 ;
struct Node {
    int next , to ;
    double w ;
} edge[ MAXN ] ;
int head[ MAXN ] , cnt ;
int n , m ;
double d[ MAXN ] ;
bool vis[ MAXN ] ;
inline int read () {
    int tot = 0 , f = 1 ; char c = getchar () ;
    while ( c < '0' || c > '9' ) { if ( c == '-' ) f = -1 ; c = getchar () ; }
    while ( c >= '0' && c <= '9' ) { tot = tot * 10 + c - '0' ; c = getchar () ; }
    return tot * f ;
}
inline void add ( int x , int y , double z ) {
    edge[ ++ cnt ].next = head[ x ] ;
    edge[ cnt ].to = y ;
    edge[ cnt ].w = z ;
    head[ x ] = cnt ;
}
inline bool spfa ( int u , double t ) {
    vis[ u ] = 1 ;
    for ( int i = head[ u ] ; i ; i = edge[ i ].next ) {
        int v = edge[ i ].to ;
        if ( d[ u ] + edge[ i ].w - t < d[ v ] ) {
            d[ v ] = d[ u ] + edge[ i ].w - t ;
            if ( vis[ v ] || spfa ( v , t ) ) return 1 ; //判负环
        }
    }
    vis[ u ] = 0 ;
    return 0 ;
}
inline bool check ( double t ) {
    for ( int i = 1 ; i <= n ; i ++ ) d[ i ] = 0 ;
    memset ( vis , 0 , sizeof ( vis ) ) ;
    for ( int i = 1 ; i <= n ; i ++ ) if ( spfa ( i , t ) ) return 1 ; //每个点都要作为起点来判一遍
    return 0 ;
}
signed main () {
    n = read () ; m = read () ;
    for ( int i = 1 ; i <= m ; i ++ ) {
        int x = read () , y = read () ;
        double z ; cin >> z ;
        add ( x , y , z ) ;
    }
    double l = -1e7 , r = 1e7 ;
    while ( r - l > 1e-12 ) { // 二分答案
        double mid = ( l + r ) / 2 ;
        if ( check ( mid ) ) r = mid ;
        else l = mid ;
    }
    printf ( "%.8lf\n" , r ) ;
    return 0 ;
}
posted @ 2020-08-10 14:41  hulean  阅读(125)  评论(0编辑  收藏  举报