D114 最短路→负环 01分数规划+SPFA 算法 P2868 [USACO07DEC] Sightseeing Cows G 观光奶牛
D114 最短路→负环 01分数规划+SPFA 算法 P2868 [USACO07DEC] Sightseeing Cows G 观光奶牛_哔哩哔哩_bilibili
P2868 [USACO07DEC] Sightseeing Cows G - 洛谷
有向图,既有点权又有边权,求一个环,使环上的点权之和除以边权之和的比值最大。
思路
最大化比值,显然是 01分数规划。我们二分一个 mid,然后判定图上是否存在一个环,该环是否满足
$\frac {\sum f(i)} {\sum w(i)} > mid$,即 $\sum (f(i)-w(i)*mid)>0$,即 $\sum (w(i)*mid-f(i))<0$
把 “点权化入边权” 计算,把 $w(i)*mid-f(i)$ 看做新边权,即等效边权之和 $<0$,即图中存在一个 “负环”
每次二分跑一遍 SPFA 判是否存在负环,若存在说明 mid 偏小,则 $l=mid$,否则 $r=mid$
点权、边权的数据范围均为 [1,1000],所以比值的范围应为 (0,1000]

玄学优化:找环,栈比队列快!因为环上的点一旦入栈,就会出栈,顺藤摸瓜,扩展一圈找出环
相关板子:
D113【模板】最短路→负环 SPFA 算法 P2850 [USACO06DEC] Wormholes G 虫洞 - 董晓 - 博客园
// 二分+SPFA 算法 O(24*N*M) #include<bits/stdc++.h> using namespace std; const int N=1010,M=5010; int h[N],to[M],ww[M],ne[M],idx; void add(int a,int b,int c){ to[++idx]=b,ww[idx]=c,ne[idx]=h[a],h[a]=idx; } int n,m; int f[N]; //点权 double d[N]; int cnt[N],vis[N]; bool spfa(double mid){ memset(d,0,sizeof d); memset(vis,0,sizeof vis); memset(cnt,0,sizeof cnt); stack<int> q; //栈比队列快 for(int i=1; i<=n; i++) q.push(i),vis[i]=true; while(!q.empty()){ int u=q.top();q.pop();vis[u]=false; for(int i=h[u]; i; i=ne[i]){ int v=to[i]; double w=ww[i]*mid-f[u]; //等效边权 if(d[v]>d[u]+w){ d[v]=d[u]+w; cnt[v]=cnt[u]+1; if(cnt[v]>=n) return true; //有负环 if(!vis[v]) q.push(v),vis[v]=true; } } } return false; } int main(){ cin>>n>>m; for(int i=1; i<=n; i++) cin>>f[i]; //点权 for(int i=0,a,b,c; i<m; i++){ cin>>a>>b>>c; add(a,b,c); } double l=0,r=1000; while(r-l>1e-4){ double mid=(l+r)/2; if(spfa(mid)) l=mid; else r=mid; } printf("%.2lf\n",r); }
浙公网安备 33010602011771号