[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 ;
}