题目链接http://vjudge.net/problem/viewProblem.action?id=34650
题目大意:
给定n个点m条边的加权有向图,求平均权值最小的回路。
平均权值=路径权值之和/路径边数
我们可以通过找到他其中的最小和最大值,然后通过二分不断查找满足的点,然后尽可能的取到它的最大值,因为这里保留两位有效小数,所以设立
while(la-st>0.001)即可
找到一个满足的值是要建立在图上的每一条线段减去这个值后便能得到一个负圈,我们通常用spfa判负圈。
这个spfa只是用来作判断并不是算最短路径,为了防止出现多个连通分量,你只从一个点开始可能遍历整个图,所以最开始就把节点全放入队列中,dp[i]的
值全设定为0,如果出现负圈,则会为了找到最小值一直循环更新,我们用cnt[]数组,当某个点被访问了n次以上时,说明出现了负圈。
具体spfa函数如下所示:
bool spfa() { for(int i=1;i<=n;i++) cnt[i]=0,visit[i]=0; visit[1]=1; queue<int> q; for(int i=1;i<=n;i++) q.push(i),dp[i]=0; while(!q.empty()){ int u=q.front(); visit[u]=0; q.pop(); for(int i=first[u];i!=-1;i=path[i].next){ if(dp[path[i].y]>dp[u]+path[i].d){ dp[path[i].y]=dp[u]+path[i].d; if(!visit[path[i].y]) { q.push(path[i].y); if(++cnt[path[i].y]>n) return true; } } } } return false; }
我们每次减去一个要找到的mid值,那我们一定要在判断结束后给它加回来以免下次判断出意外
总代码如下:
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 #include <algorithm> 5 using namespace std; 6 #define N 52 7 int first[N],visit[N],k,cnt[N],n; 8 double dp[N],maxn,minn; 9 struct Path{ 10 int y,next; 11 double d; 12 }path[100010]; 13 14 void add(int a,int b,double c) 15 { 16 path[k].y=b,path[k].d=c,path[k].next=first[a]; 17 first[a]=k++; 18 } 19 20 bool spfa() 21 { 22 for(int i=1;i<=n;i++) cnt[i]=0,visit[i]=0; 23 visit[1]=1; 24 queue<int> q; 25 for(int i=1;i<=n;i++) q.push(i),dp[i]=0; 26 while(!q.empty()){ 27 int u=q.front(); 28 visit[u]=0; 29 q.pop(); 30 for(int i=first[u];i!=-1;i=path[i].next){ 31 if(dp[path[i].y]>dp[u]+path[i].d){ 32 dp[path[i].y]=dp[u]+path[i].d; 33 if(!visit[path[i].y]) { 34 q.push(path[i].y); 35 if(++cnt[path[i].y]>n) return true; 36 } 37 } 38 } 39 } 40 return false; 41 } 42 43 bool findMid(double mid) 44 { 45 for(int i=0;i<k;i++) path[i].d-=mid; 46 bool temp=spfa(); 47 for(int i=0;i<k;i++) path[i].d+=mid; 48 return temp; 49 } 50 51 int main() 52 { 53 int T,m,a,b; 54 double c,st,la,mid,ans=-1; 55 scanf("%d",&T); 56 for(int j=1;j<=T;j++){ 57 memset(first,-1,sizeof(first)); 58 scanf("%d%d",&n,&m); 59 k=0; 60 maxn=0,minn=10000005; 61 for(int i=0;i<m;i++) 62 { 63 scanf("%d%d%lf",&a,&b,&c); 64 add(a,b,c); 65 maxn=max(maxn,c); 66 minn=min(minn,c); 67 } 68 st=minn-1,la=maxn; 69 printf("Case #%d: ",j); 70 if(!findMid(la+1)){printf("No cycle found. ");continue;} 71 while(la-st>0.001){ 72 mid=st+(la-st)/2; 73 if(findMid(mid)) la=mid; 74 else ans=mid,st=mid; 75 } 76 printf("%.2f ",ans); 77 } 78 return 0; 79 }