並不對勁的圖論專題(三):SPFA算法的優化
阿新 • • 發佈:2018-07-26
a算法 bubuko 等於 dfs size iomanip 最小 bre else if
1.bzoj1489->
這是個新套路。
我們希望找到最小的x,那麽可以二分x,然後判斷是否存在圈的邊權的平均值小於等於x。
設圈的邊權依次為w1,w2,w3,…,wk,平均值為p,
則有p= (w1+w2+w3+…+wk)/k ,
可以推出p*k=w1+w2+w3+…+wk,
這樣就會有(w1-p)+(w2-p)+…+(wk-p)=0,
當p≤x時,就會有(w1-x)+(w2-x)+…+(wk-x)≤0。
這樣,可以通過把所有邊的邊權都改為w-x,然後通過判斷負環得出存在圈的邊權的平均值小於等於x。
代碼,不知為何常數極大:
#include<algorithm> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iomanip> #include<iostream> #include<map> #include<queue> #include<stack> #include<vector> #define maxn 3010 #define maxm 10010 #define eps 1e-10 using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(isdigit(ch)==0 && ch!=‘-‘)ch=getchar(); if(ch==‘-‘)f=-1,ch=getchar(); while(isdigit(ch))x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar(); return x*f; } inline void write(int x) { int f=0;char ch[20]; if(!x){puts("0");return;} if(x<0){putchar(‘-‘);x=-x;} while(x)ch[++f]=x%10+‘0‘,x/=10; while(f)putchar(ch[f--]); putchar(‘\n‘); } int fir[maxn],nxt[maxm],v[maxm],cnt,n,m,inf[5],vis[maxn],yes; double mid,ans,L,R,dis[maxn],w[maxm]; void ade(int u1,int v1,double w1){v[cnt]=v1,w[cnt]=w1,nxt[cnt]=fir[u1],fir[u1]=cnt++;} void dfs(int u) { if(yes)return; for(int k=fir[u];k!=-1;k=nxt[k]) { if(dis[v[k]]>dis[u]+(w[k]-mid)) { dis[v[k]]=dis[u]+(w[k]-mid); if(vis[v[k]]){yes=1;return;} vis[v[k]]=1,dfs(v[k]),vis[v[k]]=0; } else if(dis[v[k]]==dis[u]+(w[k]-mid)){if(vis[v[k]]){yes=1;return;}vis[v[k]]=1,dfs(v[k]),vis[v[k]]=0;} } } int check() { yes=0; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++)dis[j]=0.0,vis[j]=0; dfs(i); if(yes)break; } return yes; } int main() { memset(inf,31,sizeof(inf)); memset(fir,-1,sizeof(fir)); n=read(),m=read();L=10000000.0,R=-10000000.0; for(int i=1;i<=m;i++){int x=read(),y=read();double z;scanf("%lf",&z);R=max(z,R),L=min(z,L);ade(x,y,z);}ans=R; while(fabs(R-L)>eps) { mid=(L+R)/2.0;int f=check(); //cout<<L<<" "<<mid<<" "<<R<<endl; if(f)ans=ans<mid?ans:mid,R=mid-eps; else L=mid+eps; } printf("%.8lf",ans); return 0; }
2.bzoj1715->
判負環就ok。
說個小技巧:一開始將所有點的距離設為0,而不是正無窮,這樣遇到負數才會更新。
說另一個小技巧:將bfs改成dfs,在判負環時可能會更優秀,但是有可能被卡,比如上一題。
#include<algorithm> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iomanip> #include<iostream> #include<map> #include<queue> #include<stack> #include<vector> #define maxn 505 #define maxm 6010 using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(isdigit(ch)==0 && ch!=‘-‘)ch=getchar(); if(ch==‘-‘)f=-1,ch=getchar(); while(isdigit(ch))x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar(); return x*f; } inline void write(int x) { int f=0;char ch[20]; if(!x){puts("0");return;} if(x<0){putchar(‘-‘);x=-x;} while(x)ch[++f]=x%10+‘0‘,x/=10; while(f)putchar(ch[f--]); putchar(‘\n‘); } int T,n,m,ww,fir[maxn],dis[maxn],nxt[maxm],v[maxm],w[maxm],vis[maxn],cnt,yes; void ade(int u1,int v1,int w1){v[cnt]=v1,w[cnt]=w1,nxt[cnt]=fir[u1],fir[u1]=cnt++;} void reset(){memset(fir,-1,sizeof(fir)),memset(vis,0,sizeof(vis));cnt=yes=0;} void dfs(int u) { if(yes)return; for(int k=fir[u];k!=-1;k=nxt[k]) { if(dis[v[k]]>dis[u]+w[k]) { // cout<<dis[v[k]]<<" "<<dis[u]<<" "<<u<<" "<<v[k]<<endl; dis[v[k]]=dis[u]+w[k]; if(vis[v[k]]){yes=1;/*cout<<v[k]<<endl;*/break;} vis[v[k]]=1; dfs(v[k]); vis[v[k]]=0; } } } int main() { T=read(); while(T--) { reset(); n=read(),m=read(),ww=read(); for(int i=1;i<=m;i++){int x=read(),y=read(),z=read();ade(x,y,z),ade(y,x,z);} for(int i=1;i<=ww;i++){int x=read(),y=read(),z=read();ade(x,y,-z);} for(int i=1;i<=n&&!yes;i++){memset(dis,0,sizeof(dis));vis[i]=1;dfs(i);vis[i]=0;} puts(yes?"YES":"NO"); } return 0; }
3.vijos1053->
板子題,代碼就不貼了。
說下SLF優化:如果有一個點的距離被更新時,小於隊列當前隊首的距離,那麽就把它放到隊首。
它的依據在於,不存在負權邊的情況下,隊首的元素比它大,更新的點不會比它更優,也就是類似dijkstra的貪心。
但是,存在負權邊時,這個優化就變得玄學了。比如點x入隊時,隊首y的距離大於x的,將x放在隊首。但是存在一條y->x的負權邊,在算y時又更新了x的距離,就讓x額外入隊了一次。
今天的超鏈接圖是銀火龍呢。
並不對勁的圖論專題(三):SPFA算法的優化