本题就是大名鼎鼎的最优比率环的问题。
我们不难注意到,平均值就是如下:
x=cnt∑ci
其中cnt 代表点个数,而ci 代表权值和。
学过分数规划的想必已经秒了,没事我们一步一步推。
根据题意有:
ans→cnt∑ci≥x=∑ci≥x×cnt=∑ci−x×cnt≥0=∑(ci−x)≥0=∑(x−ci)≤0
这是什么,负环?我们把边权改成mid−ci 不就是在让我们找一个环使得权值为负数,这不就是负环吗。
根据上面的不等式,我们不难看出我们需要二分这个x。我们用 SPFA 来判断负环,若有负环我们将x 调小,否则调大,这样就满足题意了。
接下来是代码时间,但是这里需要有几点注意:
- BFS 的 SPFA 会炸,请使用 DFS 版本的 SPFA,详情见代码。
- double 精度不够,请使用 long double。
- 时刻注意精度问题!
- 最后结果要输出2l+r,不然会被 hack 精度(没错 long double 还是不够)。
代码通过 uDebug 上所有 hack,同时讨论区内个人 hack 也通过,感谢 hack 提供者们的贡献。也欢迎来 hack 我代码 www。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| #include<bits/stdc++.h> #define double long double using namespace std; constexpr int MN=55; constexpr double eps=1e-10; struct Edge{ int v; double w; }; int n,m,tot; double l,r,dis[MN]; bool vis[MN]; vector<Edge> adj[MN];
int cmp(double x,double y){ if(fabs(x-y)<eps) return 0; if(x>y) return 1; return -1; }
bool dfs(int u,double k){ vis[u]=1; for(auto e:adj[u]){ int v=e.v; auto w=e.w; if(dis[v]>dis[u]+w-k){ dis[v]=dis[u]+w-k; if(vis[v]||dfs(v,k)){ return vis[u]=0,1; } } } return vis[u]=0; }
bool check(double k){ memset(dis,0,sizeof(dis)); for(int i=1;i<=n;i++){ if(dfs(i,k)) return 1; } return 0; }
void init(){ for(int i=1;i<=n;i++) adj[i].clear(),vis[i]=0; }
void solve(){ init(); cin>>n>>m; for(int i=1;i<=m;i++){ int u,v; double w; cin>>u>>v>>w; adj[u].push_back({v,w}); } l=0,r=1e9; while(r-l>eps){ double mid=(l+r)/2; if(check(mid)) r=mid; else l=mid; } if(cmp(l,1e9)==0) cout<<"No cycle found.\n"; else cout<<fixed<<setprecision(2)<<(l+r)/2<<'\n'; }
int main(){ int T; cin>>T; while(T--){ cout<<"Case #"<<++tot<<": "; solve(); } return 0; }
|