Dijkstra算法基础详解,附有练习题
介绍
Dijkstra算法是一种用于求解单源最短路径问题的经典算法。它可以在给定有向加权图(无向是特殊的有向)中,从一个起点到其他所有节点的最短路径。
作用:
在给定的加权图中,求一个点到其余点的最短路径
基本思想:
- 初始化一个距离数组dist[],表示起点到各个节点的当前最短距离,初始时起点的最短距离为0,其余节点的最短距离为无穷大。
- 创建一个集合S,用于存放已经确定最短路径的节点,初始时集合S为空。
- 重复以下步骤,直到集合S包含了所有节点:
- 在未确定最短路径的节点中,选择一个节点v,选择的是dist[v]值最小,将该节点加入集合S。
- 更新节点v的所有邻接节点u的最短距离,如果通过节点v到达节点u的距离比当前最短路径更短,则更新最短距离dist[u]为新的最短距离。
- 表示为: dist[u] = min(dist[u],dist[v]+edge[v][u])
Dijkstra算法的关键是如何选择节点v,这里使用了贪心策略,每次选择距离起点最近的未确定最短路径的节点,确保每次加入的节点都是当前最短距离确定的。
模板如下:
int dijkstra(int b)//求b到各个点的距离
{
memset(dist, 0x3f, sizeof dist);
dist[b] = 0;
for (int i = 0; i < n - 1; i++)
{
int t = -1; // 在还未确定最短路的点中,寻找距离最小的点
for (int j = 1; j <= n; j++)
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
//从t这个点更新到其他点的距离。
//虽然是遍历了每个点,但并不代表t可以到任意点,
//因为会把到不了的边的距离设为inf,就避免了考虑t点可以到哪些点的问题
for (int j = 1; j <= n; j++)
dist[j] = min(dist[j], dist[t] + v[t][j]);
st[t] = true;
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
Dijkstra序列
输入样例:
5 7
1 2 2
1 5 1
2 3 1
2 4 1
2 5 2
3 5 1
3 4 1
4
5 1 3 4 2
5 3 1 2 4
2 3 4 5 1
3 2 1 5 4
输出样例:
Yes
Yes
Yes
No
分析:还是要先弄目标dijkstra序列的含义是什么,这个困扰我了很久。
我们知道,在构建dist数组时,每次是选 还没有选过 且 当前dist最小 的点,在这个点的基础上进行更新。然后要选n次,我们这n次选的点的序列就称作dijkstra序列(第一个点就是起点)。
例如:5,1,3,4,2。代表点5是起点,然后选点1,点3,点4,点2。
设每次选的点为dist[i],那么dist[i]一定小于当前所有还没选过的点。以这个为判断条件,如果不满足,则不是一个dijkstra序列。
#include<assert.h>
#include<cstdio>
#include<set>
#include<list>
#include<queue>
#include<math.h>
#include<stdlib.h>
#include<string>
#include<string.h>
#include <stdio.h>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<sstream>
#include<stack>
#include <utility>
#include<map>
#include <vector>
#define inf 0x3f3f3f3f
#define int long long
const int N =1010;
//#include <bits/stdc++.h>
typedef long long ll;
#include<iostream>
using namespace std;
//long long MAX(long long a, long long b) { return a < b ? b : a; }
int n, m;
int v[N][N];//存每条边,v[i][j]代表i到j的距离。如果i到j没有直接的路径,那么设为inf
int dist[N];//存i号点到各个点的最短距离
int st[N];//存到达该点该点是否已经确定最短路径
int q[N];
int dijkstra() {//从b开始,到各个点的距离
memset(dist, inf, sizeof(dist));
memset(st, 0, sizeof(st));
dist[q[0]] = 0;//到达自己的距离是0
for (int i = 0; i < n; i++) {
int cur = q[i];
for (int j = 1; j <= n; j++) {
if (!st[j] && dist[j]<dist[cur]) {
return false;
}
}
//从cur这个点更新到其他点的距离。
//虽然是遍历了每个点,但并不代表cur可以到任意点,
//因为会把到不了的边的距离设为inf,就避免了考虑cur点可以到哪些点的问题
for (int j = 1; j <= n; j++) {
dist[j] = min(dist[j], dist[cur] + v[cur][j]);
}
st[cur] = 1;
}
return 1;
}
signed main() {
cin >> n >> m;
memset(v, inf, sizeof(v));
for (int i = 0; i < m; i++) {
int a, b, d; cin >> a >> b >> d;
//因为是无向图
v[a][b] = d;
v[b][a] = d;
}
int k; cin >> k;
while (k--) {
vector<int> t;
t.push_back(0);
for (int i = 0; i < n; i++) {
cin >> q[i];
}
if (dijkstra()) cout << "Yes" << endl;
else cout << "No" << endl;
}
return 0;
}
奶牛回家
输入样例:
5
A d 6
B d 3
C e 9
d Z 8
e Z 3
输出样例:
B 11
分析:简单题。命名为大写字母的牧场才有牛,可以逆向思考,从牛棚Z开始往这些牧场走,那条路最短。
易错点,两个牧场间可能有多条路,这些路长度可能不一样,注意判断。
#include<cstdio>
#include<set>
#include<list>
#include<queue>
#include<math.h>
#include<stdlib.h>
#include<string>
#include<string.h>
#include <stdio.h>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<sstream>
#include<stack>
#include <utility>
#include<map>
#include <vector>
#define inf 0x3f3f3f3f
#define int long long
const int N =1010;
//#include <bits/stdc++.h>
typedef long long ll;
#include<iostream>
using namespace std;
//long long MAX(long long a, long long b) { return a < b ? b : a; }
int n;
int v[N][N]; // 存储每条边
int dist[N]; // 存储1号点到每个点的最短距离
bool st[N]; // 存储每个点的最短路是否已经确定
int get(char x) {
if (x >= 'A' && x <= 'Z') return x - 'A' + 1;
return x - 'a' + 26 + 1;
}
void dijkstra() {
memset(dist, inf, sizeof(dist));
memset(st, 0, sizeof(st));
dist[26] = 0;//Z到Z距离是0
for (int i = 1; i <= 52; i++) {
int cur = -1;
for (int j = 1; j <= 52; j++) {
if (!st[j] && (cur == -1 || dist[cur] > dist[j])) {
cur = j;
}
}
for (int j = 1; j <= 52; j++) {
dist[j] = min(dist[j], dist[cur] + v[cur][j]);
}
st[cur] = 1;
}
}
signed main() {
cin >> n;
memset(v, inf, sizeof(v));
while (n--) {
char a, b; int c;
cin >> a >> b >> c;
int d = get(a);
int e = get(b);
v[d][e] = v[e][d] = min(v[d][e], c);
}
dijkstra();
int index = 0, len = inf;
for (int i = 1; i <= 25; i++) {
if (len > dist[i] && dist[i] <= 10000 * 1000) {
//如果大于这个距离就是没有到i的路,距离为inf
len = dist[i];
index = i;
}
}
printf("%c %lld", index + 'A' - 1, len);
return 0;
}