• 题目分享F 二代目


    题意:T个点R种双向边,P种单向边,求点S到每个点的最短距离

    分析:(这再看不出来是spfa就该**了)

    首先,这题能否用spfa就看他是否有负环呗,显然,双向边的权值非负,单向边还有个啥政策,总之显然是没有负环了

    那么直接跑裸的spfa

    没想到竟然t了

    难不成spfa还有优化?

    我带着怀疑的心情上了百度,艹还真有

      SLF优化:

      SLF优化,即 Small Label First  策略,使用 双端队列 进行优化。

      一般可以优化15%~20%,在竞赛中比较常用。

      设从 u 扩展出了 v ,队列中队首元素为 k ,若 dis[ v ] < dis[ k ] ,则将 v 插入队首,否则插入队尾。

      注:队列为空时直接插入队尾。

    妙啊,我加上这个优化直接就过了,代码也很好写

    #include<cstdio>
    #include<queue>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    
    const int maxm=2e5+1;
    const int maxn=3e4+1;
    const int inf=0x3f3f3f3f;
    
    struct Node
    {
        int to,next,val;
    }e[maxm];
    int head[maxn];
    int dis[maxn];
    bool vis[maxn];
    int cnt;
    
    void add(int x,int y,int z)
    {
        e[++cnt].to=y;
        e[cnt].val=z;
        e[cnt].next=head[x];
        head[x]=cnt;
    }
    
    int read()
    {
        char ch=getchar();int ans=0,p=1;
        while(ch>'9'||ch<'0')
        {
            if(ch=='-') p=-1;
            ch=getchar();
        }
        while(ch<='9'&&ch>='0')
        {
            ans=(ans<<1)+(ans<<3)+ch-'0';
            ch=getchar();
        }
        return ans*p;
    }
    
    void spfa(int x)
    {
        memset(dis,0x3f,sizeof(dis));
        deque<int> q;q.push_back(x),dis[x]=0;
        while(!q.empty())
        {
            int now=q.front();q.pop_front();
            vis[now]=0;
            for(int i=head[now];i;i=e[i].next)
            {
                int v=e[i].to;
                if(dis[v]>dis[now]+e[i].val)
                {
                    dis[v]=dis[now]+e[i].val;
                    if(!vis[v])
                    {
                        vis[v]=1;
                        if(!q.empty()&&dis[v]<dis[q.front()]) q.push_front(v);
                        else q.push_back(v);
                    }
                }
            }
        }
    }
    
    int main()
    {
        int t,r,p,s,x,y,z;
        t=read(),r=read(),p=read(),s=read();
        while(r--)
        {
            x=read(),y=read(),z=read();
            add(x,y,z),add(y,x,z);
        }
        while(p--)
        {
            x=read(),y=read(),z=read();
            add(x,y,z);
        }
        spfa(s);
        for(int i=1;i<=t;i++)
        {
            if(dis[i]==inf) printf("NO PATH
    ");
            else printf("%d
    ",dis[i]); 
        }
        return 0;
    } 

    然后我接着往下看,还有一个优化

      

      LLL优化:

      LLL优化,即 Large Label Last  策略,使用 双端队列 进行优化。

      一般用SLF+LLL可以优化50%左右,但是在竞赛中并不常用LLL优化。

      设队首元素为 k ,每次松弛时进行判断,队列中所有 dis 值的平均值为 x 。

      若 dist[ k ] > x ,则将 k 插入到队尾,查找下一元素,直到找到某一个 k 使得 dis[ k ] <= x ,则将 k 出队进行松弛操作。

    我也给他写出来了

    #include<cstdio>
    #include<queue>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    
    #define ll long long
    
    const int maxm=2e5+1;
    const int maxn=3e4+1;
    const int inf=0x3f3f3f3f;
    
    struct Node
    {
        int to,next,val;
    }e[maxm];
    int head[maxn];
    int dis[maxn];
    bool vis[maxn];
    int cnt;
    
    void add(int x,int y,int z)
    {
        e[++cnt].to=y;
        e[cnt].val=z;
        e[cnt].next=head[x];
        head[x]=cnt;
    }
    
    int read()
    {
        char ch=getchar();int ans=0,p=1;
        while(ch>'9'||ch<'0')
        {
            if(ch=='-') p=-1;
            ch=getchar();
        }
        while(ch<='9'&&ch>='0')
        {
            ans=(ans<<1)+(ans<<3)+ch-'0';
            ch=getchar();
        }
        return ans*p;
    }
    
    void spfa(int x)
    {
        int num=1;ll sum=0;
        memset(dis,0x3f,sizeof(dis));
        deque<int> q;q.push_back(x),dis[x]=0;
        while(!q.empty())
        {
            int now=q.front();q.pop_front();
            num--,sum-=dis[now];
            while(num&&dis[now]>sum/num)
            {
                q.push_back(now);
                now=q.front();
                q.pop_front();
            }
            vis[now]=0;
            for(int i=head[now];i;i=e[i].next)
            {
                int v=e[i].to;
                if(dis[v]>dis[now]+e[i].val)
                {
                    dis[v]=dis[now]+e[i].val;
                    if(!vis[v])
                    {
                        vis[v]=1;
                        if(!q.empty()&&dis[v]<dis[q.front()]) q.push_front(v);
                        else q.push_back(v);
                        num++,sum+=dis[v];
                    }
                }
            }
        }
    }
    
    int main()
    {
        int t,r,p,s,x,y,z;
        t=read(),r=read(),p=read(),s=read();
        while(r--)
        {
            x=read(),y=read(),z=read();
            add(x,y,z),add(y,x,z);
        }
        while(p--)
        {
            x=read(),y=read(),z=read();
            add(x,y,z);
        }
        spfa(s);
        for(int i=1;i<=t;i++)
        {
            if(dis[i]==inf) printf("NO PATH
    ");
            else printf("%d
    ",dis[i]); 
        }
        return 0;
    } 

    令我没想到的是,这俩加起来竟然又t了

    也可能是我写的不对,也有可能这个优化被卡了

    总之以后我写spfa一定会带上SLF优化的,

    这个题大概老姚是想让我们了解一下spfa的优化吧?

    代码:上面给过了

  • 相关阅读:
    keras_12_keras自带的Applications
    keras_11_keras中示例数据集
    keras_10_回调函数 Callbacks
    Runloop
    SDWebImage
    NSOperation
    单例模式
    GCD
    一文读懂汉明码
    聊聊SPOOLing技术
  • 原文地址:https://www.cnblogs.com/lin4xu/p/12799323.html
Copyright © 2020-2023  润新知