Dijkstra算法和Bellman Ford算法

图的两种表示方式

  1. 邻接矩阵

优点:方便查找,方便操作
缺点:需要空间过大

#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;
}
  1. 邻接链表

优点:存储空间小
缺点:查找指定边时间复杂度高

#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;
}
  1. 链式前向星

算法使用

创建一个静态数组来存储源节点到其余结点的权值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]);
}
}
}

最后遍历输出一下就行了

模板题目

click

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

练习题

P3905 道路重建

题目中有的道路被破坏掉,求修复道路使其联通的最小代价。首先道路时无向边,完好的道路的值与题解无关设置为\(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;
}
posted @   管管19  阅读(69)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示