*题解:P3199 [HNOI2009] 最小圈

题目链接

解析

考虑我刚学的分数规划。二分答案 \(x\),问题转化为判定是否有

\[\frac{\sum_{i=1}^{k}w_{c_i,c_{i + 1}}}{k} \le x \]

变形一下:

\[\sum_{i=1}^{k}(w_{c_i,c_{i + 1}} - x)\le 0 \]

将边权设为 \(w_{c_i,c_{i + 1}} - x\),要判定的就是是否有非正环,SPFA 即可。

时间复杂度 \(O(nm\log w)\)

代码

/*
*/
#include <bits/stdc++.h>
#define eps 0.0000000001
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 23000 + 5,M = 1000 + 5,P = 2000000,mod = 45989;
int head[N],to[N],nxt[N],e;
double val[N];
double dis[N];
int cnt[N],inq[N];
int n,m;
void add(int u,int v,double w){
	e++;
	val[e] = w;
	to[e] = v;
	nxt[e] = head[u];
	head[u] = e;
}
bool chk(double mid){
	queue<int> q;
	for(int i=1;i<=n;i++){
		dis[i] = 0;
		inq[i] = true;
		cnt[i] = 0;
		q.push(i);
	}
	while(!q.empty()){
		int u = q.front();
		q.pop();
		inq[u] = false;
		for(int i=head[u];i;i = nxt[i]){
			int v = to[i];
			double w = val[i];
			if(dis[v] >= dis[u] + w - mid){
				dis[v] = dis[u] + w - mid;
				if(!inq[v]){
					inq[v] = true;
					cnt[v]++;
					if(cnt[v] >= n + 5){
						return true;
					}
					q.push(v);
				}
			}
		}
	}
	return false;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int u,v;
		double w;
		cin>>u>>v>>w;
		add(u,v,w);
	}
	double l = -1e7,r = 1e7;
	while(r - l > eps){
		double mid = (l + r) / 2;
		if(chk(mid)) r = mid;
		else l = mid;
	}
	cout<<fixed<<setprecision(8)<<l;
	return 0;
}

posted @ 2026-06-03 18:32  yutar  阅读(11)  评论(0)    收藏  举报