Luogu 4779 - 【模板】单源最短路径(标准版) - [单源最短路进阶]

题目链接:https://www.luogu.org/problemnew/show/P4779

题目背景

2018 年 7 月 19 日,某位同学在 NOI Day 1 T1 归程 一题里非常熟练地使用了一个广为人知的算法求最短路。

然后呢?

100→60;

Ag→Cu;

最终,他因此没能与理想的大学达成契约。

小 F 衷心祝愿大家不再重蹈覆辙。

题目描述

给定一个 N 个点,M 条有向边的带非负权图,请你计算从 S 出发,到每个点的距离。

数据保证你能从 S 出发到任意点。

输入格式:

第一行为三个正整数 N,M,S。 第二行起 M 行,每行三个非负整数 ui​,vi​,wi​,表示从 ui​ 到 vi​ 有一条权值为 wi​ 的边。

输出格式:

输出一行 N 个空格分隔的非负整数,表示 S 到每个点的距离。

输入样例#1:
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
输出样例#1:
0 2 4 3

题解:

这是一道最短路的模板题,但是它卡SPFA,还卡某些优化不好的SPFA,所以本题我们要使用SLF+swap优化的SPFA。

(顺便再用堆优化dijkstra)

首先回顾一下Bellman-Ford算法:

①初始化,所有点的 dist[i] = INF,出发点 s 的dist[s] = 0;

②对于每条边 edge(u,v),若 dist[u] != INF,且dist[v] > dist[u] + edge(u,v).w,则松弛dist[v] = dist[u] + edge(u,v).w

③循环步骤② $\left| V \right| - 1$ 次,或者知道某一次步骤②中没有边可以松弛,则转步骤④

④若存在一条边 edge(u,v),满足 dist[u] != INF,且dist[v] > dist[u] + edge(u,v).w,则图中存在负环。

我们知道,Bellman-Ford算法的时间复杂度是 $O\left( {\left| V \right|\left| E \right|} \right)$,而我们可以使用队列对其进行优化,那就是大名鼎鼎的SPFA算法,

所以说,SPFA就是队列优化的Bellman-Ford算法。

不妨回顾一下SPFA算法:

①初始化,所有点的 dist[i] = INF,源点 s 的dist[s] = 0;构建队列,源点 s 入队,并标记该点已在队列中。

②队头出队,标记该点已不在队列中(若图存在负权边,则可以对该点出队次数检查,若出队次数大于 n,则存在负环,算法结束),

 遍历该点出发的所有边,假设当前遍历到某条边为 edge(u,v),若 dist[v] > dist[u] + edge(u,v).w,则松弛dist[v] = dist[u] + edge(u,v).w,

 检查节点 v 是否在队列中,若不在则入队,标记节点 v 已在队列中。

④重复执行步骤②直到队列为空。

但是,对于这个“队列优化”,有必要清楚的一点是:

SPFA的时间复杂度,其实和Bellman-Ford是一样的,都是$O\left( {\left| V \right|\left| E \right|} \right)$,

只是SPFA在部分图中跑的比较快,给人以 $O\left( {k\left| E \right|} \right)$ 的感觉(其中 $k$ 为所有点入队次数的平均,部分图的 $k$ 值很小),

但是,现在很多的题目,都是会卡掉SPFA的。所以,现在对于没有负权边的图,单源最短路请优先考虑堆优化Dij

当然啦,SPFA被卡了我还是想用SPFA怎么办?根据知乎上@fstqwq对于“如何看待SPFA算法已死这种说法?”的回答表明,

在不断的构造图卡SPFA和不断地优化SPFA过数据的斗争中,LLL优化、SLF优化、SLF带容错等一系列优化都被卡掉了,

而到目前(2018.9.4)为止,有位神仙想出了一种SLF+swap优化的SPFA,暂时还很难卡掉,心向往之情不自禁地就想了解一下:

首先是单纯的 SLF优化:Small Label First策略,设要入队的节点是 j,而队首元素为 i,若dist[j] < dist[i] 则将 j 插入队首,否则插入队尾。

再然后是 SLF+swap优化:每当队列改变时,如果队首节点 i 的 dist[i] 大于队尾节点 j 的 dist[j],则交换首尾节点。

SLF+swap优化的AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;

const int maxn=1e5+10;
const int INF=0x3f3f3f3f;

int n,m,s;

//邻接表存图
struct Edge{
    int u,v,w;
    Edge(int u=0,int v=0,int w=0){this->u=u,this->v=v,this->w=w;}
};
vector<Edge> E;
vector<int> G[maxn];
void addedge(int u,int v,int w)
{
    E.push_back(Edge(u,v,w));
    G[u].push_back(E.size()-1);
}

//数组模拟队列
const int Qsize=2e5+10;
int head,tail;
int Q[Qsize];

//SPFA单源最短路
int dist[maxn];
bool vis[maxn];
void spfa()
{

    for(int i=1;i<=n;i++) dist[i]=INF,vis[i]=0;
    dist[s]=0;

    head=tail=0;
    Q[tail++]=s; vis[s]=1;
    while(head<tail)
    {
        int u=Q[head++]; vis[u]=0;
        if(head<tail-1 && dist[Q[head]]>dist[Q[tail-1]]) swap(Q[head],Q[tail-1]);
        for(int i=0;i<G[u].size();i++)
        {
            Edge &e=E[G[u][i]];
            int v=e.v;
            if(dist[v]>dist[u]+e.w)
            {
                dist[v]=dist[u]+e.w;
                if(!vis[v])
                {
                    Q[tail++]=v; vis[v]=1;
                    if(head<tail-1 && dist[Q[head]]>dist[Q[tail-1]]) swap(Q[head],Q[tail-1]);
                }
            }
        }
    }
}

int main()
{
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w);
    }
    spfa();
    for(int i=1;i<=n;i++) printf("%d%s",dist[i],((i==n)?"\n":" "));
}

猜你喜欢

转载自www.cnblogs.com/dilthey/p/9583728.html