【dfs判负环】BZOJ1489: [HNOI2009]最小圈
Description
找出一个平均边权最小的圈。
Solution
经典问题,二分答案判断有无负环。
但数据范围大,普通spfa会超时,于是用dfs判负环(快多了)。
思路是dis设为0,枚举每个点u,如果d(u)+w<d(v)就搜v,如果搜到的节点曾搜到过说明找到了负环。
为什么是对的呢?对于一个负环,一定可以找到一个节点从这里开始走一直累加权值,权值一直为负。
Code
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 const int N=3e5+5,M=1e4+5; 6 7 int head[M],e[M],nxt[M],k; 8 double w[M]; 9 int adde(int u,int v,double g){ 10 e[++k]=v;w[k]=g;nxt[k]=head[u];head[u]=k; 11 } 12 int n,m; 13 14 double d[N]; 15 int vis[N],flag; 16 17 int spfa(int u){ 18 vis[u]=1; 19 for(int i=head[u];i;i=nxt[i]){ 20 int v=e[i]; 21 if(d[u]+w[i]<d[v]){ 22 if(vis[v]){flag=1;break;} 23 d[v]=d[u]+w[i]; 24 spfa(v); 25 } 26 } 27 vis[u]=0; 28 } 29 30 int jud(double mid){ 31 for(int i=1;i<=k;i++) w[i]-=mid; 32 memset(d,0,sizeof(d)); 33 memset(vis,0,sizeof(vis)); 34 flag=0; 35 int ret=0; 36 for(int i=1;i<=n;i++){ 37 spfa(i); 38 if(flag){ret=1;break;} 39 } 40 for(int i=1;i<=k;i++) w[i]+=mid; 41 return ret; 42 } 43 44 int main(){ 45 scanf("%d%d",&n,&m); 46 int u,v; double g; 47 for(int i=1;i<=m;i++){ 48 scanf("%d%d%lf",&u,&v,&g); 49 adde(u,v,g); 50 } 51 52 double l=-1e7,r=1e7; 53 while(r-l>1e-10){ 54 double mid=(l+r)/2; 55 if(jud(mid)) r=mid; 56 else l=mid; 57 } 58 printf("%.8lf",l); 59 return 0; 60 }