HNOI2019 最小圈

( ext{Problem})

对于一张有向图,要你求图中最小圈的平均值最小是多少,即若一个圈经过 (k) 个节点,那么一个圈的平均值为圈上 (k) 条边权的和除以 (k),现要求其中的最小值

( ext{Solution})

经典的分数规划题
很容易想到二分答案
那么我们要找到一个 (T) 个点的环满足
(frac{sum_{i=1}^T e_i}{T} le mid)
(T) 乘过来,移项得
(sum_{i=1}^T [e_i-mid] le 0)
于是 (dfs) 暴力寻找这样的环
这种 (dfs) 相当于 (dfs) 版本的 (spfa),不会超时

注意这份代码是 ( ext{JZOJ 5173})
题目是一样的,输出的区别罢了

( ext{Code})

#include<cstdio>
using namespace std;

const int N = 1005, M = 10005;
int n, m, vis[N], h[N];
double dis[N], mid;
struct edge{
	int to, nxt, w;
}e[M];

inline void add(int u, int v, int w)
{
	static int tot = 0;
	e[++tot] = edge{v, h[u], w}, h[u] = tot;
}

int dfs(int x)
{
	vis[x] = 1;
	for(register int i = h[x]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		double w = 1.0 * e[i].w - mid;
		if (dis[x] + w <= dis[v])
		{
			dis[v] = dis[x] + w;
			if (vis[v] || dfs(v)) return 1;
		}
	}
	vis[x] = 0;
	return 0;
}

inline int check()
{
	for(register int i = 1; i <= n; i++) vis[i] = 0, dis[i] = 0;
	for(register int i = 1; i <= n; i++) if (dfs(i)) return 1;
	return 0;
}

int main()
{
	scanf("%d%d", &n, &m);
	for(register int i = 1, u, v, w; i <= m; i++)
		scanf("%d%d%d", &u, &v, &w), add(u, v, w);
	double l = 0, r = 1e7, ans, bz = 0;
	while (r - l > 1e-12)
	{
		mid = (l + r) / 2;
		if (check()) r = mid, ans = mid, bz = 1;
		else l = mid;
	}
	if (bz) printf("%.2lf", l);
	else printf("PaPaFish is laying egg!");
}
原文地址:https://www.cnblogs.com/leiyuanze/p/14860478.html