版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40101479/article/details/81939475
- 介绍
==== - SPFA
SPFA已死
SPFA是基于Bellman - Ford的一种贼快的算法, 用队列来实现。
通常用于求含负权边的单源最短路径,以及判负权环。
SPFA 最坏情况下复杂度和朴素 Bellman-Ford 相同,为 O(VE)。
(参考百度百科)
毒瘤数据免谈
- 前向星
一个数据结构,里面的成员可以存储起点,终点和权值。
要有一个数组维护每点连出去的边的起点。
1.链式前向星构造
这里我们使用的是链式前向星,用结构体储存每一条边:
struct Edge
{
//其实还可以记录这条边的起点,但这题没必要
int next; //表示这条边所指向的下一条边
int to; //这条边的终点
int w; //权值
};
再开个first数组
first[i] 表示以i为起点的第一条边
2.加边
first[i]其实就是以i为起点的边所组成的链表的第一个元素。
每次加边,我们让first[i]这条边指向要加的边,再将所加的边更新为first[i]。
因此,我们每次遍历是倒着遍历的:
void AddEdge(int begin, int end, int w)
{
len++; //第len条边
e[len] = Edge{first[begin], end, w};
//first[begin]是当前所加的边指向的下一条边
first[begin] = len; //first是保存下标的
};
3.遍历
从第一条边开始,e[i].[next] 即为下一条边的编号,从而遍历以s为起点的所有边,当边的编号为0时即跳出循环
for (int i = first[s]; i; i = e[i].next)
- 算法
======
思想:
我们先将起点入队, 将所有与起点相连的点进行松弛操作。
如果松弛成功的点不在队列里,就把它进队。
最后队空即结束。
上代码:
#include <bits/stdc++.h>
#define INF 2147483647
#define MAXN 10000
#define MAXM 500000
using namespace std;
struct Edge { //前向星存边
int next, to, w;
//next:下一条边, to, w:此边终点和此边权值
}e[500030];
int n, m, s, x, y, w, len;
int first[MAXN + 30], d[MAXN + 30], vis[MAXN + 30];
//d[i]:起点到i的最短路, first[i]:i的第一条出边, vis[i]:记录i是否在队列里
void AddEdge(int a, int b, int v) { //加边
len++;
e[len] = Edge{first[a], b, v};
first[a] = len;
}
void SPFA() {
for (int i = 1; i <= n; ++i)
d[i] = INF;
d[s] = 0;
queue < int > q;
q.push(s);
vis[s] = 1;
//初始化
while (!q.empty() ) {
x = q.front();
q.pop();
vis[x] = 0;
for (int i = first[x]; i != 0; i = e[i].next) { //前向星遍历
y = e[i].to;
if (d[x] + e[i].w < d[y]) { // 松弛
d[y] = d[x] + e[i].w;
if (vis[y] == 0){
q.push(y);
vis[y] = 1; //标记在队列里
}
}
}
}
}
int main()
{
scanf ("%d%d%d", &n, &m, &s);
for (int i = 1; i <= m; ++i) {
scanf ("%d%d%d", &x, &y, &w);
AddEdge(x, y, w);
}
SPFA();
for (int i = 1; i <= n; ++i)
printf("%d ", d[i]);
printf("\n");
return 0;
}
撒花结束