最短路算法之SPFA(二)

目录

  • 1.先决条件
  • 2.SPFA算法
    • 2.1适用范围
    • 2.2算法流程
    • 2.3判断负环
  • 3.相关练习

1.先决条件

阅读本文前,需阅读:

  1. 最短路算法之Dijkstra(一).

2.SPFA算法

SPFA 在无负边权图大概率会被构造数据弄的AC 不了,所以尽量不使用SPFA .

关于SPFA,它死了.

2.1适用范围

SPFA适用于任何带权无向或有向图,与Dijsktra不同的是,SPFA适用于负边权图.
但要注意判断负环,负环情况下是求不出最短路径或权值的.

2.2算法流程

d[i] 表示点i 到起点s 的最短距离,初始化d = {INF} ,d[s] = 0 .
vis[i] = true 表示点i 在队列q 中,初始化vis = {false} .
将点s 加入队列q ,赋值vis[s] = true ,进入循环,直到队列q为空终止

每次循环,取出队头元素点u ,赋值vis[u] = false .
遍历点u 的所有出边{u,v,w} ,若d[v] > d[u] + w ,则更新d[v] = d[u] + w .
点v 不在队列q 中,则将点v 入队q ,vis[v] = true .

该算法时间复杂度一般情况下较优,但在CCF精心构造的数据下,很可能TLE .
在2.1适用范围中提到:“负环情况下是求不出最短路径或权值的.”,我会在2.3判断负环中阐述判断负环的办法.

code:(未判断负环,有向图)

#include <bits/stdc++.h>
using namespace std;

const int N = 10005;
const int INF = 0x3f3f3f;
vector<pair<int, int> > g[N];
int d[N];
bool vis[N];

int main()
{
    int n, m, s;
    scanf("%d %d %d", &n, &m, &s);
    for(int i = 0; i < m; i++) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        g[u].push_back({v, w});
    }
    for(int i = 1; i <= n; i++) {
        vis[i] = false;
        d[i] = INF;
    }
    d[s] = 0;
    queue<int> q;
    q.push(s);
    while(q.size()) {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(auto i : g[u]) {
            int v = i.first, w = i.second;
            if(d[v] > d[u] + w){
                d[v] = d[u] + w;
                if(!vis[v]){
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    for(int i=1;i<=n;i++) printf("%d ", d[i]);
    return 0;
}

2.3判断负环

我们知道,只要一个图是连通图,从一个点最多经过N 个点就能到达任意的另一个点.
观察发现:
如果图中不存在负环,一条路径最多经过N 个点
如果图中存在负环,则SPFA算法会一直绕着负环走.
所以,一条路径经过的节点数大于N 说明图中存在负环.

code:

//SPFA判负环版
//JiansYuan 2020.8.4
#include <bits/stdc++.h>
using namespace std;

const int N = 10005;
const int INF = 0x3f3f3f;
vector<pair<int, int> > g[N];
int d[N], cnt[N]; //cnt记录经过的节点数
bool vis[N];

int main()
{
    int n, m, s;
    scanf("%d %d %d", &n, &m, &s);
    for(int i = 0; i < m; i++) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        g[u].push_back({v, w});
    }
    for(int i = 1; i <= n; i++) {
        vis[i] = false;
        d[i] = INF;
        cnt[i] = 0;
    }
    d[s] = 0;
    queue<int> q;
    q.push(s);
    bool flag = false;//用于记录图中是否存在负环
    while(q.size()) {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(auto i : g[u]) {
            int v = i.first, w = i.second;
            if(d[v] > d[u] + w){
                d[v] = d[u] + w;
                cnt[v]++;
                if(cnt[v] > n) flag = true;
                if(!vis[v]){
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if(flag) printf("ERROR!\n");
    else for(int i=1;i<=n;i++) printf("%d ", d[i]);
    return 0;
}

3.相关练习

由于大部分题目都会卡SPFA ,所以题目比较难找QWQ .
洛谷P3371

JiansYuan
2020.8.4

猜你喜欢

转载自blog.csdn.net/qq_41095952/article/details/107777523