zoukankan      html  css  js  c++  java
  • 题目分享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的优化吧?

    代码:上面给过了

  • 相关阅读:
    查看mysql版本的四种方法及常用命令
    newInstance和new的区别(good)
    Citrix 服务器虚拟化之六 Xenserver虚拟机创建与快照
    Java实现 蓝桥杯 算法训练 排序
    Java实现 蓝桥杯 算法训练 排序
    Java实现 蓝桥杯 算法训练 排序
    Java实现 蓝桥杯 算法训练 2的次幂表示
    Java实现 蓝桥杯 算法训练 2的次幂表示
    Java实现 蓝桥杯 算法训练 前缀表达式
    Java实现 蓝桥杯 算法训练 前缀表达式
  • 原文地址:https://www.cnblogs.com/lin4xu/p/12799323.html
Copyright © 2011-2022 走看看