单源最短路
- 起点固定,求到终点的最短路径长度
- 默认求 1 ∼ n 1\sim n 1∼n的最短路径, n n n代表点的数量; d i s t [ n ] dist[n] dist[n]即为答案
- 看的过程可以结合最后一节的完整流程理解
- 后期有时间会加一些讲解
图的存储
本节是下面章节的基础
- N N N代表顶点数, M M M代表边数
邻接矩阵
int g[N][N];
// g[a][b] = c 代表a->b且边权为c
int main()
{
memset(g, 0x3f, sizeof g);
while (m --)
{
int a, b, c;
g[a][b] = min(g[a][b], c);
// 无向图, 加一句
// g[b][a] = min(g[a][b], c);
}
}
邻接表
int h[N], e[M], w[M], ne[M], idx;
// add(a, b, c) 代表a->b且边权为c
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
void traversal(int a) // 遍历a结点连到的所有边
{
for (int i = h[a]; i != -1; i = ne[i])
{
int b = e[i], c = w[i]; // a->b且边权为c
// ...
}
}
int main()
{
memset(h, -1, sizeof h); // 初始化
while (m --)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
// 无向边的话, 加一句
// add(b, a, c)
}
}
结构体
struct Edge
{
int a, b, c;
} edges[N];
// edges[i] = {a, b, c} 代表a->b且边权为c
如何选择建图方式
图论的题一般会有一个顶点的数量 N N N和一个边的数量 M M M
- 当 N < 1 0 4 N \lt 10^4 N<104,可以用邻接矩阵
- 当
N
≥
1
0
5
N \ge 10^5
N≥105,一定得用邻接表
- 如存储的是无向图,记得 M × 2 M\times 2 M×2
- 当题目的要求仅为"要不重不漏遍历所有边",则可以用结构体存储
最短路方法选择
可根据以下特征结合题目自行选择
- 所有边权为正
Dijkstra
- 朴素, O ( N 2 ) O(N^2) O(N2)
- 堆优化, O ( M ⋅ log N ) O(M\cdot\log{N}) O(M⋅logN)
- 存在负权边
Bellman-Ford
, O ( N ⋅ M ) O(N \cdot M) O(N⋅M),一般不用这个,所以略过SPFA
,一般 O ( M ) O(M) O(M),最坏 O ( N ⋅ M ) O(N\cdot M) O(N⋅M)
Dijkstra
朴素版
用邻接矩阵实现
int dist[N]; // 存储1~i的最短距离
bool st[N]; // 代表i是否已找到最短距离
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 0; i < n; i ++)
{
int t = -1;
for (int j = 1; j <= n; j ++)
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
st[t] = true;
for (int j = 1; j <= n; j ++)
dist[j] = min(dist[j], dist[t] + g[t][j]);
}
return dist[n];
}
堆优化版
用邻接表实现
int dist[N]; // 存储1~i的最短距离
bool st[N]; // 代表i是否已找到最短距离
int dijkstra()
{
priority_queue<PII, vector<PII>, greater<PII>> q;
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
q.push({0, 1});
while (q.size())
{
auto t = q.top();
q.pop();
int node = t.second;
if (st[node]) continue;
st[node] = true;
for (int i = h[node]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[node] + w[i])
{
dist[j] = dist[node] + w[i];
q.push({dist[j], j});
}
}
}
return dist[n];
}
SPFA
用邻接表实现
int dist[N]; // 存储1~i的最短距离
bool st[N]; // 代表i是否在队列中
int spfa()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
queue<int> q;
q.push(1);
st[1] = true;
while (q.size())
{
int t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];
if (!st[j])
{
q.push(j);
st[j] = true;
}
}
}
}
return dist[n];
}
附录
给大家一个小的测试样例和完整的一个流程;可以把中间的方法换成其他的跑跑看
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 1000010, M = N;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
int dijkstra()
{
priority_queue<PII, vector<PII>, greater<PII>> q;
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
q.push({0, 1});
while (q.size())
{
auto t = q.top();
q.pop();
int node = t.second;
if (st[node]) continue;
st[node] = true;
for (int i = h[node]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[node] + w[i])
{
dist[j] = dist[node] + w[i];
q.push({dist[j], j});
}
}
}
return dist[n];
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while (m --)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%d\n", dijkstra());
return 0;
}
输入:
3 3
1 2 2
2 3 1
1 3 4
输出:
3