SPFA算法,单源最短路径(含负边权)

点击查看代码
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
#pragma warning(disable:4996)
const int maxn = 510; //最多不超过500个顶点
const int INF = 0x3fffffff; //表示无穷大

//SPFA伪代码
queue<int> q;
源点s入队
while (队列q非空) {
	取出队首元素u;
	for (遍历u的所有邻接边) {
		if (d[u] + dis < d[v]) { //如果以u为中转点,可以使起点与v距离更短
			d[v] = d[u] + dis; //更新d[v]
			if (v不在队列中) {
				将v入队;
				if (v入队次数大于n-1) 则表示图中存在从源点出发的负环 算法返回false,然后根据题目要求存在负环时的情况进行处理
			}
		}
	}
}
否则while循环结束后,算法返回true,表示图中不存在从源点出发的负环

//SPFA算法是Bellman的优化版本,解决单源最短路径(含有负边权),时间复杂度是O(ke),k是常数,很多情况下不超过2,e是边数
struct Node {
	int v, dis; //邻接表的目标顶点,dis是边权
};
vector<Node> adj[maxn]; //邻接表存储图G
int n, d[maxn]; //顶点数,起点到各点的最短距离
int num[maxn]; //记录每个顶点的入队次数,如果事先知道图中不存在环,那么可以省略
bool inq[maxn]; //记录每个顶点是否存在队列中

bool SPFA(int s) { //起点是s
	fill(num, num + maxn, 0); //初始时num[]全部为0,每个顶点入队次数为0
	fill(inq, inq + maxn, false); //初始时inq[]全部为false,每个顶点都不在队列中
	fill(d, d + maxn, INF); //初始时d[]全部为无穷大,表示顶点与各点都不相连
	queue<int> q; //定义队列q
	q.push(s); //起点入队
	inq[s] = true; //设置起点在队列中
	num[s]++; //起点入队次数加一
	d[s] = 0; //起点与自己的距离为0
	//主体部分
	while (!q.empty()) { //当队列为空时,不需要再继续遍历,算法结束
		int u = q.front(); //将队首顶点存入u中
		q.pop(); //队首顶点出队后要手动删除
		inq[u] = false; //u出队后,设置u不在队列中		
		for (int j = 0; j < adj[u].size(); j++) { //遍历u的所有邻接边v
			int v = adj[u][j].v; //为了方便书写用v和dis记录邻接点和边u->v的边权
			int dis = adj[u][j].dis;
			if (d[u] + dis < d[v]) { //如果以u为中转点,可以使起点与v距离更短
				d[v] = d[u] + dis; //更新d[v]
				if (!inq[v]) { //如果v不在队列中
					q.push(v); //将v入队
					inq[v] = true; //设置v已在队列中
					num[v]++; //v入队次数加一
					//如果v入队次数大于n-1,则表示图中存在从源点出发的负环,算法返回false,然后根据题目要求存在负环时的情况进行处理
					if (num[v] >= n) return false; 
				}
			}
		}
	}
	return true; //否则返回true,表示图中不存在从源点出发的负环
}
posted @ 2022-09-30 22:49  zhaoo_o  阅读(8)  评论(0编辑  收藏  举报