#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
typedef pair<int, int>PII;
const int N = 1e4 + 10, INF = 2147483647;
int dist[N];
bool st[N];
vector<PII>edges[N];
int n, m, s;
void dijkstra()
{
for (int i = 0; i <= n; i++) dist[i] = INF;
dist[s] = 0;
//循环n次找n个顶点的边权情况
for (int i = 1; i < n; i++)
{
//找最小边权
int u = 0;
for (int j = 1; j <= n; j++)
if (!st[j] && dist[j] < dist[u]) u = j;
//打上标记然后松弛
st[u] = true;
for (auto &t : edges[u])
{
int v = t.first, w = t.second;
if (dist[v] > dist[u] + w)
dist[v] = dist[u] + w;
}
}
for (int i = 1; i <= n; i++) cout << dist[i] << " ";
}
int main()
{
cin >> n >> m >> s;
for (int i = 1; i <= m; i++)
{
int x, y, z; cin >> x >> y >> z;
edges[x].push_back({ y, z });
}
dijkstra();
return 0;
}
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
typedef pair<int, int> PII;
//创建小根堆
priority_queue<PII, vector<PII>, greater<PII>> heap;
vector<PII> edges[N];
int n, m, s;
int dist[N];
bool st[N];
void dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[s] = 0;
heap.push({ 0, s });//权值,顶点
while (heap.size())
{
auto t = heap.top();
heap.pop();
int v = t.second;
if (st[v]) continue;
st[v] = true;
for (auto& x : edges[v])
{
int u = x.first, w = x.second;
if (dist[u] > dist[v] + w)
{
dist[u] = dist[v] + w;
heap.push({ dist[u], u });
}
}
}
for (int i = 1; i <= n; i++) cout << dist[i] << " ";
}
int main()
{
cin >> n >> m >> s;
for (int i = 1; i <= m; i++)
{
int x, y, z;
cin >> x >> y >> z;
edges[x].push_back({ y, z });
}
dijkstra();
return 0;
}
但我们的dijkstra算法遇到了负边权时:
这时我们得采用其他算法了。
bellman-ford 算法
dijkstra算法以点出发,而这个bf算法则以边出发,这个算法可以解决边权为负数的情况。
算法核⼼思想:不断尝试对图上每⼀条边进⾏松弛,直到所有的点都⽆法松弛为⽌。
这个依然只能过第一个题目。
代码展示:
cpp复制代码
#include <iostream>
#include <vector>
using namespace std;
typedef pair<int, int> PII;
const int N = 1e4 + 10, INF = 2147483647;
vector<PII> edges[N];
int dist[N];
int n, m, s;
void bf()
{
for (int i = 0; i <= n; i++) dist[i] = INF;
dist[s] = 0;
// 最多进行 n - 1 次松弛操作
for (int i = 1; i < n; i++)
{
// 每一轮开始时将 flag 初始化为 false
bool flag = false;
for (int j = 1; j <= n; j++)
{
int u = j;
for (auto& t : edges[u])
{
int v = t.first, w = t.second;
if (dist[u] == INF) continue;
if (dist[u] + w < dist[v])
{
dist[v] = dist[u] + w;
flag = true;
}
}
}
// 每一轮结束后判断是否有边被松弛,若没有则提前结束
if (!flag) break;
}
for (int i = 1; i <= n; i++) cout << dist[i] << " ";
}
int main()
{
cin >> n >> m >> s;
for (int i = 1; i <= m; i++)
{
int x, y, z;
cin >> x >> y >> z;
edges[x].push_back({ y, z });
}
bf();
return 0;
}
spfa 算法
该算法依旧是对bf算法的优化,这里使用队列对bf算法进行优化。
在bf算法中很多时候我们不需要进行那么多的无用的松弛操作:
1.只有上一次被松弛的节点,它的出边,才可能引起下一次的松弛操作;
2.因此,如果用队列来维护"那些节点可能会引起松弛操作",就能只访问必要的边了,降低了时间复杂度
虽然在⼤多数情况下 spfa 跑得很快,但其最坏情况下的时间复杂度为 。将其卡到这个复杂度
也是不难的,所以在没有负权边时最好使⽤ Dijkstra 算法。
cpp复制代码
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
typedef pair<int, int>PII;
const int N = 1e4 + 10, INF = 2147483647;
vector<PII>edges[N];
int dist[N];
bool st[N];
queue<int> q;
int n, m, s;
void spfa()
{
for (int i = 0; i <= n; i++) dist[i] = INF;
dist[s] = 0;
q.push(s);
st[s] = true;
while (q.size())
{
auto u = q.front(); q.pop();
st[u] = false;
for (auto& t : edges[u])
{
int v = t.first, w = t.second;
if (dist[u] == INF) continue;
if (dist[u] + w < dist[v])
{
dist[v] = dist[u] + w;
if (!st[v])
{
q.push(v);
st[v] = true;
}
}
}
}
for (int i = 1; i <= n; i++) cout << dist[i] << " ";
}
int main()
{
cin >> n >> m >> s;
for (int i = 1; i <= m; i++)
{
int x, y, z; cin >> x >> y >> z;
edges[x].push_back({ y, z });
}
spfa();
return 0;
}