zoukankan      html  css  js  c++  java
  • 并不对劲的图论专题(三):SPFA算法的优化

    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('
    ');
    }
    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('
    ');
    }
    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额外入队了一次。

    今天的超链接图是银火龙呢。

  • 相关阅读:
    es6---let和const
    node.js开发指南系列(1)partial is not defined
    input唤起键盘影响移动端底部fixed定位
    vue滑动吸顶以及锚点定位
    nodejs开发准备工作(2)
    nodejs开发准备工作(1)
    php基础小知识
    php基础
    git基础
    ps基础
  • 原文地址:https://www.cnblogs.com/xzyf/p/9370668.html
Copyright © 2011-2022 走看看