bzoj 1486: [HNOI2009]最小圈

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\n",ans);
}

int main()
{
	work();
	return 0;
}

posted @ 2017-10-23 22:29  PIPIBoss  阅读(179)  评论(3编辑  收藏  举报