P1768 天路 SPFA判负环+二分
题意:给出一个图,每条边有两个权值,一个是观赏价值、一个是费用
要我们求出一个环,要让(总的观赏价值/总的费用)的值最大
思路:我们二分来做这道题,对象为(总的观赏价值/总的费用),然后跑SPFA判负环;
那么为什么是判负环呢
我们设一个环的总观赏价值为V,费用为W
那么要想让当前查询对象满足,就要求出来的值>=mid
即:V/W>=mid
故: V>=mid*W
故:mid*W-V<=0
所以,我们每次跑SPFA的时候,把每条边的权值改为mid*w-v,然后开始跑SPFA
很明显,当出现负权环的时候,满足题意
当大于0的时候,不满足
当一个环的值等于0的时候,也满足题意,不过在这种情况下,我们默认不满足
那为什么默认不满足也可以ac呢?这就要考虑到精度问题了。
题目给出的精度只到了0.1,假如刚好搜(0.1)有一个环等于0 满足情况的话
我们便会向左区间搜索,然后既然0.1满足情况,那么0.099999肯定也满足,这个时候就会是个负数环,而不是环等于0了
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=7007; 4 struct edge 5 { 6 int to,v,c; 7 }; 8 vector<edge>G[maxn]; 9 int n,m; 10 bool vis[maxn]; 11 double dis[maxn]; 12 bool spfa(double ans,int now)//DFS版的SPFA 13 { 14 vis[now]=true; 15 for(int i=0;i<G[now].size();i++){ 16 edge e=G[now][i]; 17 double x=ans*e.c-e.v;//边的权值,根据二分出来的ans进行修改 18 if (dis[e.to]>dis[now]+x){ 19 if (vis[e.to]) return false; 20 else{ 21 dis[e.to]=dis[now]+x; 22 vis[now]=true; 23 if (!spfa(ans,e.to)) return false; 24 } 25 } 26 } 27 vis[now]=false;//记得要回溯 28 return true; 29 } 30 int main() 31 { 32 scanf("%d%d",&n,&m); 33 for(int i=1;i<=m;i++){ 34 int x,y,v,c; 35 scanf("%d %d %d %d",&x,&y,&v,&c); 36 G[x].push_back((edge){y,v,c}); 37 } 38 for(int i=1;i<=n;i++){ 39 G[0].push_back((edge){i,0,0});//超级点与每个点都需要联通 40 } 41 double l=0,r=1000001;//注意这里,l需要从0而不是1开始枚举。具体为什么自己想。 42 while(l+0.00001<r)//浮点数的精度问题,应该不需要我强调了? 43 { 44 memset(dis,127,sizeof(dis)); 45 memset(vis,false,sizeof(vis)); 46 dis[0]=0;vis[0]=true; 47 double mid=(l+r)/2; 48 if (spfa(mid,0)) r=mid; 49 else l=mid; 50 mid=(l+r)/2; 51 } 52 if(l==0) printf("-1\n"); 53 else printf("%.1f\n",l); 54 return 0; 55 }