一. 所有边权都是正数 (带记录答案路径)
(1)dijkstra(朴素版) o ( n ^ 2 )
· 稠密图(边数多) m 和 n ^ 2 是一个级别
#include<bits/stdc++.h>
#define LL long long
using namespace std;
struct node
{
int v, w;
};
const int N = 5e4 + 5, INF = 0x3f3f3f3f;
int n, m, s, ans, d[N], vis[N];
vector<node> G[N];
void dijkstra(int s)
{
memset(d, 0x3f, sizeof(d));
d[s] = 0;
for (int i = 1; i <= n; i ++)
{
int u = 0;
for (int j = 1; j <= n; j ++)
if (d[j] < d[u] && !vis[j]) // 标记的作用是判断当前节点是否已经更新过其邻点
u = j;
vis[u] = 1; // 每个点只可能去更新一次其邻点
for (int j = 0; j < G[u].size(); j ++)
{
int v = G[u][j].v , w = G[u][j].w;
d[v] = min(d[v], d[u] + w);
}
}
}
int main()
{
cin >> n >> m >> s;
for (int i = 1; i <= m; i ++)
{
int u, v, w;
cin >> u >> v >> w;
G[u].push_back({v, w});
}
dijkstra(s);
return 0;
}
(2)dijkstra(堆优化版) o ( m log n )
· 稀疏图 m 和 n 一个级别
#include<bits/stdc++.h>
#define LL long long
using namespace std;
struct node
{
int v, w;
};
const int N = 5e4 + 5, INF = 0x3f3f3f3f;
int n, m, s, ans, d[N], vis[N], pre[N];
vector<node> G[N];
priority_queue<pair<int, int> > pq;
void dijkstra(int s)
{
memset(d, 0x3f, sizeof(d));
d[s] = 0;
pq.push({0, s});
while (pq.size())
{
auto t = pq.top();
pq.pop();
int u = t.second;
for (int i = 0; i < G[u].size(); i ++)
{
int v = G[u][i].v, w = G[u][i].w;
if (vis[v]) // 一个队列中某个点可能会多次出现 只会取其中 d 最小的去更新 其余的都不会用到
continue;
vis[v] = 1;
if(d[u] + w < d[v])
{
d[v] = d[u] + w;
pre[v] = u; // 记录当前点的前驱节点
pq.push({-d[v], v});
}
}
}
}
void path(int u)
{
if (u == s)
{
cout << u << " ";
return;
}
path(pre[u]); // 递归输出路径
cout << u << " ";
}
int main()
{
cin >> n >> m >> s;
for (int i = 1; i <= m; i ++)
{
int u, v, w;
cin >> u >> v >> w;
G[u].push_back({v, w});
}
dijkstra(s);
path(n);
return 0;
}
二. 存在负权边(无 bellmanford) 仅介绍 spfa o ( m )
#include<bits/stdc++.h>
#define LL long long
using namespace std;
struct node
{
int v, w;
};
const int N = 5e4 + 5, INF = 0x3f3f3f3f;
int n, m, s, ans, d[N], cnt[N], vis[N], pre[N];
queue<int> q; // 队列里存的是有资格去更新其他的点的点
vector<node> G[N];
bool spfa(int s)
{
memset(d, 0x3f, sizeof(d));
d[s] = 0, vis[s] = 1, q.push(s);
while (q.size())
{
int u = q.front();
q.pop(), vis[u] = 0;
for (int i = 0; i < G[u].size(); i ++)
{
int v = G[u][i].v, w = G[u][i].w;
if (d[u] + w < d[v])
{
d[v] = d[u] + w;
cnt[v] = cnt[u] + 1; // 能到达当前点的点数加一
pre[v] = u; // 记录前驱结点
}
if (cnt[v] >= n) // 共有n个点 但能到达当前点的点数超过了n
return true; // 有负环
if (!vis[v]) // 只有本轮被更新过的点下一轮才有可能去更新其他的点
q.push(v), vis[v] = 1; // 如果该点还不在队列中 就加入队列
}
}
return false; // 无负环
}
void path(int u)
{
if (u == s)
{
cout << u << " ";
return;
}
path(pre[u]);
cout << u << " ";
}
int main()
{
cin >> n >> m >> s;
for (int i = 1; i <= m; i ++)
{
int u, v, w;
cin >> u >> v >> w;
G[u].push_back({v, w});
}
spfa(s);
path(n);
return 0;
}