图论·Bellman_ford算法

无负权回路例题
带负权回路例题

Bellman_ford算法

适用条件

  • 单源最短路径
  • 存在负权值边
  • 检测负权回路

核心操作

  • 松弛:对每条边与源点的距离重新计算
cpp 复制代码
if (dist[item.v1] != INT_MAX && dist[item.v1] + item.w < dist[item.v2]) {
			dist[item.v2] = dist[item.v1] + item.w;//dist[item.v1]未更新时应该跳过
}
  • 迭代:可以视为一种逼近 ,利用边的信息一层层迭代,每一层都利用了上一层迭代的结果,所以源点到达各个结点的距离会越来越接近最小值,最终达到终点的距离也会接近最小值(但这好像不是贪心的思想,而只是简单的迭代逼近)

个人代码

cpp 复制代码
using namespace std;
using ll = long long;
int n, m, s, t, v;
struct Edge {
	int v1, v2, w;
};
void solve() {
	cin >> n >> m;
	vector<Edge>edges;
	vector<int>dist(n + 1, INT_MAX); dist[1] = 0;
	while (m--) {
		cin >> s >> t >> v;
		edges.push_back({ s,t,v });
	}
	for (int i = 1; i < n; i++) {
		for (auto item : edges) {
			if (dist[item.v1] != INT_MAX && dist[item.v1] + item.w < dist[item.v2]) {
				dist[item.v2] = dist[item.v1] + item.w;//dist[item.v1]未更新时应该跳过
			}
		}
	}
	if (dist[n] == INT_MAX) {
		cout << "unconnected";
	}
	else {
		cout << dist[n];
	}
}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0); std::cout.tie(0);
	solve();
	return 0;
}

判断负权回路

输出结果前多加上一层循环,改掉判断逻辑即可

cpp 复制代码
for (auto item : edges) {
	if (dist[item.v1] != INT_MAX && dist[item.v1] + item.w < dist[item.v2]) {
		cout << "circle";
		return;//return不可以省略
	}
}

注意事项

  • dist[item.v1] != INT_MAX不可以省略
  • vector<Edge>edges;存储方式既不是邻接矩阵也不是邻接表

优化版SPFA

核心操作

  • 队列操作:类似BFS,目的是防止对一个出更新的边进行无意义的操作
  • 存储方式:邻接表

个人代码

cpp 复制代码
using namespace std;
using ll = long long;
int n, m, s, t, v;
struct Edge {
	int vex, weight;
};
void solve() {
	cin >> n >> m;
	vector<int>dist(n + 1, INT_MAX); dist[1] = 0;
	vector<list<Edge>>grid(n + 1, list<Edge>());
	queue<Edge>q;
	while (m--) {
		cin >> s >> t >> v;
		grid[s].push_back({ t,v});
	}
	q.push({ 1,0 });
	while (!q.empty()) {
		Edge cur = q.front();
		q.pop();
		for (auto item : grid[cur.vex]) {
			if (dist[cur.vex] + item.weight < dist[item.vex]) {
				dist[item.vex] = dist[cur.vex] + item.weight;
				q.push(item);
			}
		}
	}
	if (dist[n] == INT_MAX) {
		cout << "unconnected";
	}
	else {
		cout << dist[n];
	}
}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0); std::cout.tie(0);
	solve();
	return 0;
}

判断负权回路

只要一个顶点被加入到队列的次数>=n,一定出现了负权回路

  • 定义计数数组
cpp 复制代码
vector<int>count(n + 1, 0);
  • 初始化队列
cpp 复制代码
q.push({ 1,0 }); count[1]++;
  • 队列操作逻辑更新
cpp 复制代码
while (!q.empty()) {
	Edge cur = q.front();
	q.pop();
	if (count[cur.vex] >= n) {//判断加入队列的次数
		cout << "circle";
		return;
	}
	for (auto item : edges[cur.vex]) {
		if (dist[cur.vex] + item.weight < dist[item.vex]) {
			dist[item.vex] = dist[cur.vex] + item.weight;
			q.push(item);
			count[item.vex]++;
		}
	}
}

时间复杂度

  • 朴素版Bellman_ford算法:O(NM),N是顶点数,M是边数
  • SPFA算法:O(KN),N是顶点数,K是个不定值,取决于总共加入多少条边到队列中去
    • 进出队列的时间不计

本文参考于代码随想录

相关推荐
卡戎-caryon30 分钟前
【数据结构】06.栈&&队列
c语言·数据结构·算法·链表
山脚ice41 分钟前
【CT】LeetCode手撕—704. 二分查找
算法·leetcode
贱贱的剑1 小时前
【算法】选择排序
算法·rust·排序算法
瑜陀1 小时前
2024.06.30 刷题日记
数据结构·算法·leetcode
Star Patrick1 小时前
*算法训练(leetcode)第二十天 | 39. 组合总和、40. 组合总和 II、131. 分割回文串
c++·算法·leetcode
光久li1 小时前
【算法刷题 | 动态规划14】6.28(最大子数组和、判断子序列、不同的子序列)
算法·动态规划
飘然渡沧海1 小时前
gbk,ucs-2转中文
java·开发语言·算法
raykingl1 小时前
154. 寻找旋转排序数组中的最小值 II(困难)
java·python·算法·二分查找
raykingl1 小时前
69. x 的平方根(简单)
java·python·算法·二分查找
阳光男孩011 小时前
力扣974.和可被K整除的子数组
数据结构·算法·leetcode