递归型SPFA判负环 + 最优比例环 || [Usaco2007 Dec]奶牛的旅行 || BZOJ 1690 || Luogu P2868
题外话:最近差不多要退役,复赛打完就退役回去认真读文化课。
题面:P2868 [USACO07DEC]观光奶牛Sightseeing Cows
题解:最优比例环
题目实际是要求一个ans,使得对于图中任意一个环满足 sig(i=1,n)v[i]/sig(i=1,n)e[i]<=ans
所以将公式变换为:sig(i=1,n)v[i]-[(sig(i=1,n)v[i])*ans]<=0
sig(i=1,n)(v[i]-ans*e[i])<=0
最终化为:sig(i=1,n)(ans*e[i]-v[i])>=0,即以ans*e[i]-v[i]为新的边权重建图,对于图中任意一个环都能满足该条件的即为ans
所以二分答案,对于每个mid重建图,用递归型SPFA判断负环,若sig(i=1,n)(mid*e[i]-v[i])<0 则说明mid<ans,否则说明mid>=ans
代码:
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 const int maxn=1050,maxm=5050,inf=(1<<30)-5; 5 const double jd=1e-3; 6 int N,M,num_edge=0,edge_head[maxn],u,v; 7 double V[maxn],e,l,r,mid,Dis[maxn]; 8 bool vis[maxn],flag; 9 struct Edge{ int to,nx;double e,dis; }edge[maxm]; 10 inline void Add_edge(int from,int to,double e){ 11 edge[++num_edge].nx=edge_head[from]; 12 edge[num_edge].to=to; 13 edge[num_edge].e=e; 14 edge_head[from]=num_edge; 15 return; 16 } 17 inline void Rebuild(){ 18 for(int i=1;i<=N;i++) 19 for(int j=edge_head[i];j;j=edge[j].nx) 20 edge[j].dis=edge[j].e*mid-V[edge[j].to]; 21 return; 22 } 23 inline void SPFA(int x){ 24 if(flag) return; 25 vis[x]=1; 26 for(int i=edge_head[x];i;i=edge[i].nx){ 27 int y=edge[i].to; 28 if(Dis[y]>Dis[x]+edge[i].dis){ 29 if(vis[y]){ 30 flag=1; 31 return; 32 } 33 Dis[y]=Dis[x]+edge[i].dis; 34 SPFA(y); 35 } 36 } 37 vis[x]=0; 38 } 39 inline bool Check(){ 40 for(int i=1;i<=N;i++) vis[i]=0,Dis[i]=0; 41 flag=0; 42 for(int i=1;i<=N;i++){ 43 SPFA(i); 44 if(flag) break; 45 } 46 return flag; 47 } 48 int main(){ 49 scanf("%d%d",&N,&M); 50 for(int i=1;i<=N;i++) scanf("%lf",&V[i]); 51 for(int i=1;i<=M;i++){ 52 scanf("%d%d%lf",&u,&v,&e); 53 Add_edge(u,v,e); 54 } 55 l=0; r=10000; 56 while(l+jd<r){ 57 mid=(l+r)/2; 58 Rebuild(); 59 if(Check()) l=mid; 60 else r=mid; 61 } 62 printf("%.2lf\n",l); 63 return 0; 64 }
By:AlenaNuna