题意
给定一个n个点m条边的加权有向图,求平均权值最小的回路。
n <= 50
题解
有点像强连通分量,但是SCC只会求出最大的环。所以不能用。
这里二分答案,然后将所有边减去二分的答案,然后跑SPFA,如果有负环,说明答案有效。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 55;
typedef pair<int,double> pii;
#define to first
#define w second
vector<pii> G[maxn];
int n,m, cnt[maxn], inq[maxn];
double d[maxn];
void add(int u, int v,int w) {
G[u].push_back(pii(v,w));
}
bool spfa() {
queue<int> que;
memset(cnt,0,sizeof cnt);
memset(inq,0,sizeof inq);
for(int i = 0; i < n; ++i) {
d[i] = 0;
inq[0] = true;
que.push(i);
}
while(!que.empty()) {
int u = que.front();
que.pop();
inq[u] = false;
for(int i = 0; i < G[u].size(); ++i) {
pii &e = G[u][i];
// cout << e.w << endl;
if(d[e.to] > d[u]+e.w) {
d[e.to] = d[u]+e.w;
if(!inq[e.to]) {
que.push(e.to);
inq[e.to] = true;
if(++cnt[e.to] > n)
return true;
}
}
}
}
return false;
}
bool ok(double x) {
for(int i = 0; i < n; ++i)
for(int j = 0; j < G[i].size(); ++j) {
G[i][j].second -= x;
}
bool flag = spfa();
for(int i = 0; i < n; ++i)
for(int j = 0; j < G[i].size(); ++j) {
G[i][j].second += x;
// cout << G[i][j].second << endl;
}
return flag;
}
int main() {
int T;
scanf("%d", &T);
for(int kas = 1; kas <= T; ++kas) {
scanf("%d%d", &n, &m);
for(int i = 0; i < n; ++i)
G[i].clear();
int u,v,w;
for(int i = 0; i < m; ++i) {
scanf("%d%d%d", &u, &v, &w);
u--;v--;
add(u,v,w);
}
double l = 0, r = 10000000;
double ans = r+1;
for(int i = 0; i < 1000; ++i) {
double mid = (l+r)/2;
if(ok(mid)) {
// cout << mid << endl;
ans = mid;
r = mid;
}else
l = mid;
}
printf("Case #%d: ", kas);
if(ans == r+1)
puts("No cycle found.");
else
printf("%.2lf\n", ans);
}
return 0;
}