图论·Bellman_ford算法

发布于:2024-06-28 ⋅ 阅读:(17) ⋅ 点赞:(0)

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

Bellman_ford算法

适用条件

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

核心操作

  • 松弛:对每条边与源点的距离重新计算
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]未更新时应该跳过
}
  • 迭代:可以视为一种逼近,利用边的信息一层层迭代,每一层都利用了上一层迭代的结果,所以源点到达各个结点的距离会越来越接近最小值,最终达到终点的距离也会接近最小值(但这好像不是贪心的思想,而只是简单的迭代逼近)

个人代码

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;
}

判断负权回路

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

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,目的是防止对一个出更新的边进行无意义的操作
  • 存储方式:邻接表

个人代码

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,一定出现了负权回路

  • 定义计数数组
vector<int>count(n + 1, 0);
  • 初始化队列
q.push({ 1,0 }); count[1]++;
  • 队列操作逻辑更新
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是个不定值,取决于总共加入多少条边到队列中去
    • 进出队列的时间不计

本文参考于代码随想录


网站公告

今日签到

点亮在社区的每一天
去签到