Dijkstra算法和Bellman Ford算法
图的两种表示方式
- 邻接矩阵
优点:方便查找,方便操作
缺点:需要空间过大
#define MAX_N 1000 #define ufr(i, x, y) for (int i = x; i <= y; ++i) #define lfr(i, x, y) for (int i = x; i >= y; --i) int G[MAX_N]; signed main(){ cin >> n; ufr(i,1,n){ int from,to,cost; cin >> from >> to >> cost; G[from][to]=cost; } return 0; }
- 邻接链表
优点:存储空间小
缺点:查找指定边时间复杂度高
#define MAX_N 1000 #define ufr(i, x, y) for (int i = x; i <= y; ++i) #define lfr(i, x, y) for (int i = x; i >= y; --i) // 使用priority_queque时默认为最小堆 struct vertex { int from, to, cost; vertex(int from_, int to_, int cost_) : from(from_), to(to_), cost(cost_) {} vertex(int to_, int cost_) : to(to_), cost(cost_) {} vertex(int from_, int to_, bool token) : from(from_), to(to_) {} bool operator<(const vertex& v) const { return this->cost > v.cost; } bool operator==(const vertex& v) const { return this->from == v.from && this->to == v.to; } }; int n,m; ll dis[MAX_N]; vector<vertex> G[MAX_N]; signed main() { n = f.r(), m = f.r(); ufr(i, 1, m) { from = f.r(), to = f.r(), cost = f.r(); G[from].emplace_back(to, cost); } return 0; }
算法使用
创建一个静态数组来存储源节点到其余结点的权值
ll dis[MAX_N];
首先将源结点到其本身的权值设置为0,其余设为正无穷,然后将其放入一个小根堆构成的优先队列中
fill(goto(dis, n), 2147483647); dis[s] = 0; priority_queue<vertex> que; que.emplace(s, dis[s]);
然后开始循环当\(que\)为空时结束循环,每次循环从\(que\)中取出距离到达源点距离最小的权值结点,首先进行判断,因为后面的操作都是增加操作,所有如果当前到达源点的权值小于本点,直接\(continue\)。然后遍历该结点,依次放入比现在到达源点的权值小的结点和其权值
while (!que.empty()) { vertex v = que.top(); que.pop(); if (dis[v.to] < v.cost) continue; for (const vertex& ver : G[v.to]) { if (dis[ver.to] > v.cost + ver.cost) { dis[ver.to] = v.cost + ver.cost; que.emplace(ver.to, dis[ver.to]); } } }
最后遍历输出一下就行了
模板题目
#include <bits/stdc++.h> #define _i i << 1 #define __i i << 1 | 1 #define cl tree[i].l #define cr tree[i].r #define mod(x) ((x) % MOD) #define lowbit(x) (x & -x) #define g1(x) get<0>(x) #define g2(x) get<1>(x) #define g3(x) get<2>(x) #define ufr(i, x, y) for (int i = x; i <= y; ++i) #define lfr(i, x, y) for (int i = x; i >= y; --i) #define all(x) x.begin(), x.end() #define goto(x, y) x + 1, x + y + 1 #define mm(x, y) memset(x, y, sizeof(x)) using namespace std; using ld = long double; using ll = long long; using pp = pair<int, int>; namespace Solution { class fastio { public: template <class T = int> inline T r() noexcept { T x = 0, w = 1; char ch = 0; for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar()) x = (x << 3) + (x << 1) + (ch - '0'); return x * w; } inline string rs() noexcept { string res; char ch = 0; for (ch = getchar(); ch == '\0' || ch == '\n' || ch == '\r'; ch = getchar()) ; for (; ch != '\n' && ch != '\r'; ch = getchar()) res.append(1, ch); return res; } inline char rc() noexcept { char ch = getchar(); for (; ch == '\0' || ch == '\n' || ch == '\r' || ch == ' '; ch = getchar()) ; return ch; } template <class T = int> inline fastio& pt(T x) noexcept { static char sta[128]; int top = 0; if (x < 0) x = -x, putchar('-'); do { sta[top++] = x % 10, x /= 10; } while (x); while (top) putchar(sta[--top] + 48); return *this; } inline fastio& pts(const string s) noexcept { for (int i = 0; s[i] != '\0'; ++i) putchar(s[i]); return *this; } inline fastio& ptc(const char c) noexcept { putchar(c); return *this; } inline fastio& ln() noexcept { putchar('\n'); return *this; } } f; // 使用priority_queque时默认为最小堆 struct vertex { int from, to, cost; vertex(int from_, int to_, int cost_) : from(from_), to(to_), cost(cost_) {} vertex(int to_, int cost_) : to(to_), cost(cost_) {} vertex(int from_, int to_, bool token) : from(from_), to(to_) {} bool operator<(const vertex& v) const { return this->cost > v.cost; } bool operator==(const vertex& v) const { return this->from == v.from && this->to == v.to; } }; struct pp_hash { template <class T1, class T2> std::size_t operator()(const std::pair<T1, T2>& p) const { auto h1 = std::hash<T1>{}(g1(p)); auto h2 = std::hash<T2>{}(g2(p)); return h1 ^ h2; } }; // less --> return __x < __y; || greater --> return __x > __y; #define MAX_N 1001000 static constexpr ll MOD = 1e9 + 7; static constexpr ll eps = -1e9; static constexpr ll inf = 0x7fffffff; int n, m, s, to, from, cost; ll dis[MAX_N]; vector<vertex> G[MAX_N]; void solve() { priority_queue<vertex> que; que.emplace(s, dis[s]); while (!que.empty()) { vertex v = que.top(); que.pop(); if (dis[v.to] < v.cost) continue; for (const vertex& ver : G[v.to]) { if (dis[ver.to] > v.cost + ver.cost) { dis[ver.to] = v.cost + ver.cost; que.emplace(ver.to, dis[ver.to]); } } } ufr(i, 1, n) f.pt(dis[i]).ptc(' '); } } // namespace Solution signed main() { using namespace Solution; unordered_map<pp, int, pp_hash> mp; n = f.r(), m = f.r(), s = f.r(); ufr(i, 1, m) { from = f.r(), to = f.r(), cost = f.r(); G[from].emplace_back(to, cost); } fill(goto(dis, n), 2147483647); dis[s] = 0; solve(); return 0; }
练习题
题目中有的道路被破坏掉,求修复道路使其联通的最小代价。首先道路时无向边,完好的道路的值与题解无关设置为\(0\),被损坏的道路不变,求\((a,b)\)最短路径即可。
Bellman Ford算法
\(D(i,j)=min(D(i,j),D(i,v)+D(v,j))\)
从\(i\)到\(j\)的距离为(从\(i\)到另一个顶点\(v\)加上从\(v\)到\(j\)的距离)与(\(i\)原本到\(j\)的距离最小值)
每次循环都对能松弛的俩点都松弛,直到不能松弛为止,最坏情况下,一次循环松弛一个边,那么最多\(n-1\)次循环就可以求解,如果第\(N\)次仍然松弛,说明有负环
for (var i = 0; i < n - 1; i++) { for (var j = 0; j < m; j++) {//对m条边进行循环 var edge = edges[j]; // 松弛操作 if (distance[edge.to] > distance[edge.from] + edge.weight ){ distance[edge.to] = distance[edge.from] + edge.weight; } } }
存储边,然后无限循环每条边,直到不能更改dis数组为止
\(dis\)代表任意顶点\(V\)到原点\(S\)的距离
\(token=true\) 代表如果本次循环不能修改任何值,则最短路结果已经得到,终止循环。每次遍历从一个边的终点在\(dis\)中的值,是否大于该边出发点在\(dis\)中的值加上这条边的权值。如果要判断负环,设置一个\(cnts\)数组,当一个顶点在\(dis\)数组中的值被改变,让\(++cnts[i]\),如果没有负环,一个边最多被加\(n-1\)次
while (true) { bool token = true; ufr(i, 1, M) if (dis[G[i].to] > dis[G[i].from] + G[i].cost) { dis[G[i].to] = dis[G[i].from] + G[i].cost; //判断负环 if (++cnts[G[i].to] > N) { f.pts("Forever love").ln(); return; } token = false; } if (token) break; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了