Description
对于一张有向图,要你求图中最小圈的平均值最小是多少,即若一个圈经过k个节点,那么一个圈的平均值为圈上k条边权的和除以k,现要求其中的最小值
solution
正解:二分答案+spfa
容易想到二分答案,可以考虑边权同减或同除 (mid)。如果除,需要找出一个环长等于边权和的环,不好处理
但是减去 (mid) 就会产生负环,那么直接spfa判负环即可,这题很诡,要写dfs版spfa?还有这种操作?
写法和平时差不多,只不过只要发现当前点x能够更新的点u在队列中,那么可以断定产生了负环,效率略高?
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=3005,M=20005,inf=1e9;
const double eps=1e-9;
int head[N],nxt[M],to[M],num=0,n,m;double dis[M];
struct edge{int x,y;double z;}e[M];
il void link(int x,int y,double z){
nxt[++num]=head[x];to[num]=y;dis[num]=z;head[x]=num;}
il void Clear(){memset(head,0,sizeof(head));num=0;}
double f[N];bool vis[N];
il bool dfs(RG int x){
int u;vis[x]=1;
for(int i=head[x];i;i=nxt[i]){
u=to[i];
if(f[x]+dis[i]<f[u]){
if(vis[u]){vis[u]=0;return false;}
f[u]=f[x]+dis[i];
if(!dfs(u))return false;
}
}
vis[x]=0;
return true;
}
bool check(double mid){
Clear();RG int i;
for(i=1;i<=m;i++)
link(e[i].x,e[i].y,e[i].z-mid);
for(i=1;i<=n;i++)f[i]=0,vis[i]=0;
for(i=1;i<=n;i++)
if(!dfs(i))return true;
return false;
}
void work()
{
double l=0,r=0,mid,ans;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%lf",&e[i].x,&e[i].y,&e[i].z);
r+=e[i].z;
}
while(l<=r-eps){
mid=(l+r)/2;
if(check(mid))ans=mid,r=mid-eps;
else l=mid+eps;
}
printf("%.8lf
",ans);
}
int main()
{
work();
return 0;
}