单源最短路径算法,可以用于解决带负边权的情况。是队列优化的Bellman-Ford算法,时间复杂度O(nm)。
朴素spfa算法模板
#include
#include
const int N = 1010, M = 2e6, INF = 1e9;
int n, m; //n是节点数,m是边数
int dist[N], q[N]; //dist[i]表示源点到i点的最短距离
int h[N], to[M], w[M], ne[M], idx; //idx初始化为0
bool st[N]; //储存每个点是否在队列中
//添加边表示a到b有一条单向边,权值为c
void add(int a, int b, int c){
to[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
//求源点s到其它点的最短路径
void spfa(int s){
int hh, tt; //队列头指针和尾指针
memset(st, false, sizeof(st));
for(int i = 1; i <= n; i++) dist[i] = INF;
dist[s] = 0;
q[tt++] = s;
st[s] = true;
while(hh != tt){
//队列不为空
int t = q[hh++];
st[t] = false;
if(hh == N) hh = 0;
for(int i = h[i]; ~i; i = ne[i]){
if(dist[t] + w[i] < dist[to[i]]){
dist[to[i]] = dist[t] + w[i];
if(!st[to[i]]){
st[to[i]] = true;
q[tt++] = to[i];
if(tt == N) tt = 0;
}
}
}
}
}
int main(void){
int a, b, c;
scanf("%d %d %d", &n, &m, &s);
memset(h, -1, sizeof(h));
for(int i = 1; i <= m; i++){
scanf("%d %d %d", &a, &b, &c);
add(a, b, c);
}
spfa(s);
for(int i = 1; i <= n; i++){
if(dist[i] == INF) puts("NO PATH");
else printf("%d\n", dist[i]);
}
return 0;
}
SLF优化的SPFA算法
SLF 优化:将普通队列换成双端队列,每次将入队结点距离和队首比较,如果更大则插入至队尾,否则插入队首
#include
#include
#include
using namespace std;
const int N = 1010, M = 2e6, INF = 1e9;
int dist[N];
int h[N], to[M], w[M], ne[M], idx;
bool st[N];
int n, m;
void add(int a, int b, int c){
to[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void spfa(int s){
memset(st, false, sizeof(st));
for(int i = 1; i <= n; i++) dist[i] = INF;
dist[s] = 0;
deque q;
q.push_front(s);
st[s] = true;
while(!q.empty()){
int t = q.front();
q.pop_front();
st[t] = false;
for(int i = h[t]; ~i; i = ne[i]){
if(dist[t] + w[i] < dist[to[i]]){
dist[to[i]] = dist[t] + w[i];
if(!st[to[i]]){
if(!q.empty() && dist[to[i]] < dist[q.front()]){
q.push_front(to[i]);
}
else q.push_back(to[i]);
st[to[i]] = true;
}
}
}
}
}