图论·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是个不定值,取决于总共加入多少条边到队列中去
    • 进出队列的时间不计

本文参考于代码随想录

相关推荐
算法歌者19 分钟前
[算法]入门1.矩阵转置
算法
林开落L34 分钟前
前缀和算法习题篇(上)
c++·算法·leetcode
远望清一色35 分钟前
基于MATLAB边缘检测博文
开发语言·算法·matlab
tyler_download36 分钟前
手撸 chatgpt 大模型:简述 LLM 的架构,算法和训练流程
算法·chatgpt
SoraLuna1 小时前
「Mac玩转仓颉内测版7」入门篇7 - Cangjie控制结构(下)
算法·macos·动态规划·cangjie
我狠狠地刷刷刷刷刷1 小时前
中文分词模拟器
开发语言·python·算法
鸽鸽程序猿1 小时前
【算法】【优选算法】前缀和(上)
java·算法·前缀和
九圣残炎1 小时前
【从零开始的LeetCode-算法】2559. 统计范围内的元音字符串数
java·算法·leetcode
YSRM1 小时前
Experimental Analysis of Dedicated GPU in Virtual Framework using vGPU 论文分析
算法·gpu算力·vgpu·pci直通
韭菜盖饭2 小时前
LeetCode每日一题3261---统计满足 K 约束的子字符串数量 II
数据结构·算法·leetcode