Bellman-Ford算法,单源最短路径(含负边权)

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

//Bellman-Ford算法需要遍历所有边,使用邻接表时间复杂度是O(ne),n是顶点个数,e是边数。使用邻接矩阵会变为O(n^3)
struct Node {
	int v, dis; //邻接表的目标顶点,dis是边权
};
vector<Node> adj[maxn]; //邻接表存储图G
int n; //顶点数
int d[maxn]; //起点到各点的最短距离

bool Bellman(int s) { //起点是s
	fill(d, d + maxn, INF); //初始时d[]全部为无穷大,表示顶点与各点都不相连
	d[s] = 0; //起点与自己的距离为0
	for (int i = 0; i < n - 1; i++) { //对图中所有边进行n-1轮操作
		for (int u = 0; u < n; u++) { //每轮依次访问每个顶点的所有邻接边
			for (int j = 0; j < adj[u].size(); j++) { //逐条访问顶点u的邻接边
				int v = adj[u][j].v; //邻接边另一个顶点为v
				int dis = adj[u][j].dis; //邻接边边权
				if (d[u] + dis < d[v]) { //如果以u为中转点,能使起点与v距离更短,发现最短路径
					d[v] = d[u] + dis; //更新d[v]
				}
			}
		}
	}
	//如果图G中不存在负环,那么执行完上面代码后d[]就存储了最短路径。以下代码是判断图G中是否存在负环
	//再对所有顶点的邻接边进行一轮操作,检查是否d[]是否还有更短距离。如果有则表示d[]是与负边权相加才变得更小(正数+负数等于更小的数)
	for (int u = 0; u < n; u++) { //每轮依次访问每个顶点的所有邻接边
		for (int j = 0; j < adj[u].size(); j++) { //逐条访问顶点u的邻接边
			int v = adj[u][j].v; //邻接边另一个顶点为v
			int dis = adj[u][j].dis; //邻接边边权
			if (d[u] + dis < d[v]) { //如果还能使d[v]更优,就表示在计算中d[u]与负边权相加变得更小
				return false; //图中存在负环,返回false,之后再根据题目要求如果出现负环如何处理
			}
		}
	}
	return true; //否则返回true表示没有负环,d[]中存储了起点与各点之间的最短距离
}
//如果在不存在负环的情况下使用Bellman算法求解最短路径,或者求解多重标尺时的最短路径,求解原理与迪杰斯特拉算法的原理一样,只要省略判断负环那一段代码,
//然后添加判断第二标尺的代码即可。只有求解最短路径条数时不一样,因为Bellman算法会多次访问已被访问过的顶点,因此要设置set<int> pre[maxn]记录每个顶点的前驱顶点(去重)
//当遇到一条和已有最短路径长度相同的路径时,需要重新计算最短路径条数。例题PTA A1003
posted @ 2022-09-30 22:48  zhaoo_o  阅读(6)  评论(0编辑  收藏  举报