单源最短路入门(Dijkstra + SPFA)

单源最短路入门(Dijkstra + SPFA)


SPFA

SPFA很像BFS
(1)起点s入队,计算它所有邻居到s的最短距离(当前最短距离,不是全局最短距离,把计算一个节点到s的最短路径称为更新状态)。把s出队,状态有更新的节点入队。也就是说队列中只处理更新的节点,其他节点无影响不处理。
(2)现在队列头部是s的一个邻居u。弹出u,更新其他所有邻居的状态,把其中有变化的节点入队。
(3)这里要注意后面计算中u可能还会更新,因此只要节点状态有变化,就可以入队处理。
(4)继续上述过程,直到队列为空。

但是SPFA的稳定性是不稳定的,可能只有很少的节点重复入队也可能有很多。竞赛中常用SPFA的不稳定性卡SPFA,因此没有负权节点时优先使用较稳定的Dijkstra

SPFA模板(邻接表+队列)

洛谷 P3371 【模板】单源最短路径(弱化版)

#include <bits/stdc++.h>
using namespace std;
#define N 500001
const int INF = INT_MAX;
using namespace std;
struct edge
{
    int to;
    int w;
};
vector<edge> e[N];//e[i]:存第i个节点连接的所有边
queue<int> q;
bool vis[N];
int n, m, s;
long long dis[N];
void spfa()
{
    for (int i = 1; i <= n; i++)
    {
        dis[i] = INF;
        vis[i] = false;
    }//初始化
    q.push(s);//初始边入队
    vis[s] = true;
    dis[s] = 0;
    while (!q.empty())
    {
        int t = q.front();
        q.pop();//队头出队
        vis[t] = false;
        for (int i = 0; i < e[t].size(); i++)//遍历邻边
        {
            int to = e[t][i].to;
            if (dis[to] > dis[t] + e[t][i].w)//t的第i个邻居更近
            {
                dis[to] = dis[t] + e[t][i].w;
                if (!vis[to])
                {
                    q.push(to);
                    vis[to] = false;
                }
            }
        }
    }
}
int main()
{
    cin >> n >> m >> s;
    for (int i = 1; i <= m; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        e[a].push_back(edge{b, c});//有向图
    }
    spfa();
    for (int i = 1; i <= n; i++)
        cout << dis[i] << " ";
    return 0;
}

Dijkstra

Dijkstra用了贪心的思想,因此不能处理负边权,稳定而高效。
程序的主要内容是维护两个集合,即已确定最短路径的节点集合A,这些节点向外扩散的邻居节点集合B。

程序思路如下:
(1)把起点s放到A中,把s所有的邻居放到B中。此时,邻居到s的距离就是直连距离。
(2)从B中找到距离起点s最短的节点u,放到A中。
(3)把u所有的新邻居放到B中。显然,u的每一条边都连接了一个邻居,每个新邻居都要加进去。其中u的一个新邻居v,它到s的距离dis(s,v)等于dis(s,u)+dis(u,v)。
(4)重复(2)(3),直到B为空时结束。

Dijkstra模板(堆优化版邻接表+优先队列)

洛谷 P4779 【模板】单源最短路径(标准版)

#include <bits/stdc++.h>
using namespace std;
const int INF = INT_MAX;
const int N = 200005;
struct edge
{
    int from, to, w;
    edge(int a, int b, int c)
    {
        from = a;
        to = b;
        w = c;
    }
};
vector<edge> e[N];//e[i]:存第i个节点连接的所有边
struct node
{
    int id, dis;//id节点 与 dis距离
    node(int a, int b)
    {
        id = a;
        dis = b;
    }
    bool operator<(const node &a) const
    {
        return dis > a.dis;
    }
};
int n, m, s;
void djk()
{
    int dis[N];
    bool done[N];
    for (int i = 1; i <= n; i++)
    {
        dis[i] = INF;
        done[i] = false;
    }//初始化
    dis[s] = 0;
    priority_queue<node> v;
    v.push(node(s, 0));
    while (!v.empty())
    {
        node u = v.top();
        v.pop();
        int id = u.id;
        if (done[id])
            continue;丢弃已找到最短路径的节点,即集合A中节点
        done[id] = true;
        for (int i = 0; i < e[id].size(); i++)//遍历邻点
        {
            edge y = e[id][i];
            if (done[y.to])
                continue;//丢弃已找到最短路径的节点
            if (dis[y.to] > y.w + u.dis)
            {
                dis[y.to] = y.w + u.dis;
                v.push(node(y.to, dis[y.to]));//扩展新的邻居先放到优先队列里
            }
        }
    }
    for (int i = 1; i <= n;i++)
        cout << dis[i] << " ";
}
int main()
{
    ios::sync_with_stdio(0);
    cin >> n >> m >> s;
    for (int i = 1; i <= m;i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        e[a].push_back(edge(a, b, c));
    }
    djk();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值