spfa判負環(01分數規劃)
阿新 • • 發佈:2021-08-25
spfa 判斷負環的話
1.某個點出隊n次,存在負環。
2.某條路徑上的邊數大於等於n,說明存在負環。(通常採用這種)
0 1 分數劃分的話(通常配合二分做)
比如 題目求∑wf[i] /∑wt[i] > mid ==> ∑( wf [i] - mid * wt[i] ) > 0 ,那這樣就是在求是否存在 正環。
而通常的spfa時判斷存不存在負環 那怎麼變呢, 第一種 可以把上面 不等式填個負號, 轉化成求 負環,第二種 就是 我轉變一下思路,假如存在負環,假如 當前這個點是t,它的鄰接點是就 j ,dist[j]的更新條件是dist[t]+(wf [i] - mid * wt[i])< dist[ j ],那麼同樣的假如存在正環,我們只要改變一下更新判斷的條件, dist[t]+(wf [i] - mid * wt[i])>dist[ j ] ,我們就更新一下 dist[ j ] 。
這裡其實有個trick,就是當資料很多的時候,看更新的點數已經是原來的幾十倍的話,就有大概率是環,經驗值。
題目的話就是,
AcWing 361
觀光奶牛
//0 1分數規劃 #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=1010,M=5010; int n,m; int wf[N]; int h[N],e[M],ne[M],wt[M],idx; int q[N],cnt[N]; double dist[N]; bool st[N];bool check(double mid) { memset(st,0,sizeof st); memset(cnt,0,sizeof cnt); int hh=0,tt=0; for(int i=1;i<=n;++i) { q[tt++]=i; st[i]=true; } while(hh!=tt) { int t=q[hh++]; if(hh==N) hh=0; st[t]=false; for(inti=h[t];i;i=ne[i]) { int j=e[i]; if(dist[j]<dist[t]+wf[t]-mid*wt[i]) { dist[j]=dist[t]+wf[t]-mid*wt[i]; cnt[j]=cnt[t]+1; if(cnt[j]>=n) return true; if(!st[j]) { q[tt++]=j; if(tt==N) tt=0; st[j]=true; } } } } return false; } void add(int a,int b ,int c) { e[++idx]=b,ne[idx]=h[a],h[a]=idx,wt[idx]=c; } int main() { cin>>n>>m; for(int i=1;i<=n;++i) scanf("%d",&wf[i]); while(m -- ) { int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,c); } double l=0,r=1000; while(r-l>1e-4) { double mid=(l+r)/2; if(check(mid)) l=mid; else r=mid; } printf("%.2lf",l); return(0); }
AcWing 1165
單詞環
(把一個點拆成 一條邊 點數就會下降到26*26=676個,然後還要加小trick才能過)
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=700,M=1e5+10; int n,m; int q[N],cnt[N]; double dist[N]; int h[N],e[M],ne[M],w[M],idx; bool st[N]; void add(int a,int b,int c) { e[++idx]=b,ne[idx]=h[a],h[a]=idx,w[idx]=c; } bool check(double mid) { memset(st,0,sizeof st); memset(cnt,0,sizeof cnt); int hh=0,tt=0; for(int i=0;i<676;++i) //26*26=676 { q[tt++]=i; st[i]=true; } int count=0; while(hh!=tt) { int t=q[hh++]; if(hh==N) hh=0; st[t]=false; for(int i=h[t];i;i=ne[i]) { int j=e[i]; if(dist[j] < dist[t]+w[i]-mid) { dist[j]=dist[t]+w[i]-mid; cnt[j]=cnt[t]+1; if(++ count > 10000) return true; //trick 經驗值 if(cnt[j]>= N) return true; if(!st[j]) { q[tt++]=j; if(tt==N) tt=0; st[j]=true; } } } } return false; } int main() { char str[1010]; while(scanf("%d",&n),n) { idx=0; memset(h,0,sizeof h); for(int i=0;i<n;++i) { scanf("%s",str); int len=strlen(str); int a=(str[0] - 'a') * 26 + str[1] - 'a'; int b=(str[len-2] - 'a') * 26 + str[len-1] - 'a'; add(a,b,len); } if(!check(0)) puts("No solution"); else{ double l=0,r=1001; while(r-l>1e-4) { double mid=(l+r)/2; if(check(mid)) l=mid; else r=mid; } printf("%.2lf\n",l); } } return(0); }