当前位置: 首页 > news >正文

一刷代码随想录(图论10)

Bellman_ford 队列优化算法

题意:

题目描述

某国为促进城市间经济交流,决定对货物运输提供补贴。共有 n 个编号为 1 到 n 的城市,通过道路网络连接,网络中的道路仅允许从某个城市单向通行到另一个城市,不能反向通行。

网络中的道路都有各自的运输成本和政府补贴,道路的权值计算方式为:运输成本 - 政府补贴。

权值为正表示扣除了政府补贴后运输货物仍需支付的费用;权值为负则表示政府的补贴超过了支出的运输成本,实际表现为运输过程中还能赚取一定的收益。

请找出从城市 1 到城市 n 的所有可能路径中,综合政府补贴后的最低运输成本。

如果最低运输成本是一个负数,它表示在遵循最优路径的情况下,运输过程中反而能够实现盈利。

城市 1 到城市 n 之间可能会出现没有路径的情况,同时保证道路网络中不存在任何负权回路。

负权回路是指一系列道路的总权值为负,这样的回路使得通过反复经过回路中的道路,理论上可以无限地减少总成本或无限地增加总收益。

输入描述

第一行包含两个正整数,第一个正整数 n 表示该国一共有 n 个城市,第二个整数 m 表示这些城市中共有 m 条道路。

接下来为 m 行,每行包括三个整数,s、t 和 v,表示 s 号城市运输货物到达 t 号城市,道路权值为 v(单向图)。

输出描述

如果能够从城市 1 到连通到城市 n, 请输出一个整数,表示运输成本。如果该整数是负数,则表示实现了盈利。如果从城市 1 没有路径可达城市 n,请输出 "unconnected"。

输入示例:

6 7
5 6 -2
1 2 1
5 3 1
2 5 2
2 4 -3
4 6 4
1 3 5

解法:当没有负权回路时可以考虑使用该方法,即将每次不将所有边进行松弛,因为每次实际上只需要松弛已经计算过的点相连的边即可,使用队列管理节点,若节点minDist更新,则加入队列,直到队列为空结束。

#include <iostream>
#include <vector>
#include <queue>
#include <list>
#include <climits>
using namespace std;struct Edge { //邻接表int to;  // 链接的节点int val; // 边的权重Edge(int t, int w): to(t), val(w) {}  // 构造函数
};int main() {int n, m, p1, p2, val;cin >> n >> m;vector<list<Edge>> grid(n + 1); vector<bool> isInQueue(n + 1); // 加入优化,已经在队里里的元素不用重复添加// 将所有边保存起来for(int i = 0; i < m; i++){cin >> p1 >> p2 >> val;// p1 指向 p2,权值为 valgrid[p1].push_back(Edge(p2, val));}int start = 1;  // 起点int end = n;    // 终点vector<int> minDist(n + 1 , INT_MAX);minDist[start] = 0;queue<int> que;que.push(start); while (!que.empty()) {int node = que.front(); que.pop();isInQueue[node] = false; // 从队列里取出的时候,要取消标记,我们只保证已经在队列里的元素不用重复加入for (Edge edge : grid[node]) {int from = node;int to = edge.to;int value = edge.val;if (minDist[to] > minDist[from] + value) { // 开始松弛minDist[to] = minDist[from] + value; if (isInQueue[to] == false) { // 已经在队列里的元素不用重复添加que.push(to);isInQueue[to] = true;}}}}if (minDist[end] == INT_MAX) cout << "unconnected" << endl; // 不能到达终点else cout << minDist[end] << endl; // 到达终点最短路径
}

如果可能有负权回路则应该判断,若该节点在本次加入是时已经加入队列n-1次,那么必然出现环路,直接退出遍历。

若经过节点数目最大为k,则最大松弛次数只能为k+1次,若使用SPFA优化算法则应该将加入队列的节点全部弹出,使用while循环控制松弛次数,同时可以不将重复节点重复加入队列计算

代码:

#include <iostream>
#include <vector>
#include <queue>
#include <list>
#include <climits>
using namespace std;struct Edge { //邻接表int to;  // 链接的节点int val; // 边的权重Edge(int t, int w): to(t), val(w) {}  // 构造函数
};int main() {int n, m, p1, p2, val;cin >> n >> m;vector<list<Edge>> grid(n + 1); // 邻接表// 将所有边保存起来for(int i = 0; i < m; i++){cin >> p1 >> p2 >> val;// p1 指向 p2,权值为 valgrid[p1].push_back(Edge(p2, val));}int start, end, k;cin >> start >> end >> k;k++;vector<int> minDist(n + 1 , INT_MAX);vector<int> minDist_copy(n + 1); // 用来记录每一次遍历的结果minDist[start] = 0;queue<int> que;que.push(start); // 队列里放入起点int que_size;while (k-- && !que.empty()) {vector<bool> visited(n + 1, false); // 每一轮松弛中,控制节点不用重复入队列minDist_copy = minDist; que_size = que.size(); while (que_size--) { int node = que.front(); que.pop();for (Edge edge : grid[node]) {int from = node;int to = edge.to;int price = edge.val;if (minDist[to] > minDist_copy[from] + price) {minDist[to] = minDist_copy[from] + price;if(visited[to]) continue; // 不用重复放入队列,但需要重复松弛,所以放在这里位置visited[to] = true;que.push(to);}}}}if (minDist[end] == INT_MAX) cout << "unreachable" << endl;else cout << minDist[end] << endl;
}


http://www.mrgr.cn/news/16264.html

相关文章:

  • 高级java每日一道面试题-2024年8月31日-框架篇[Spring篇]-你对Spring事务传播机制了解多少?
  • uboot
  • 第 20 章 DOM 进阶
  • Spring Boot 中 AOP 的实用举例
  • Vue——认识day06_class与style绑定
  • 常见框架报错信息
  • Java基础 - 12 - 多线程
  • 点点数据JS逆向:k值加密扣webpack代码和纯算实现
  • 计量校准中溯源方法会有哪些不足之处?
  • 足球大小球预测及足球大数据之机器学习预测大小球
  • 带娃赚钱两不误,用AI做故事绘本,零成本轻松变现
  • opencascade 重叠曲线设置优先显示
  • JS设计模式之“幽灵工厂” - 抽象工厂模式
  • 计算机组成原理:实验四常规型微程序控制器组成实验
  • 滴滴前端日常实习一面
  • 2024最新最全:【计算机自学网站】大全,零基础入门到精通,看完这一篇就够了!
  • 前端性能优化:提升网站加载速度的五个关键技巧
  • 浅谈 Android 15 新 API:确保 TextView 完整展示、不被切断~
  • 初识Arduino
  • 【js逆向专题】3.hook技术