首先,dijkstra算法是贪心算法中的一种。主要是运用于求最短路径的问题。Dijkstra算法的思路一般为,1构建一个图把所有点到点的距离存下来。2.先随便找某一点到其他点的距离。(一般默认从1开始)3.在这些距离中找个最短的,然后松弛一次再找出最短的,所谓的松弛操作就是,遍历一遍看通过刚刚找到的距离最短的点作为中转站会不会更近,如果更近了就更新距离,这样把所有的点找遍之后就存下了起点到其他所有点的最短距离。
根据这张图我们可以画出这样一个表
然后用dis来存储并根据情况变化
与点1最近的为2,接下来比对其余点到点2再到点1的距离与该点到1的距离,若比原来的小,就进行更改。
可以发现2到3的距离加上1到2的距离是比1到3的距离短,就将原来的无穷大改为
10(1->2)+50(2->3)=60 再重复上面的步骤寻找与点2最近的点直到所有点都被用过
完整的变化图:
最后一排也就是我们要的结果。
可以看出1到2最短路径为10 到3为50 到4为30 到5为60.
下面借用蓝桥杯2021年的一道结果填空题来说明一下。题目是这样的:
小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图 中的最短路径。
小蓝的图由 2021 个结点组成,依次编号 1 至 2021。
对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21,则两个结点 之间没有边相连;如果 a 和 b 的差的绝对值小于等于 21,则两个点之间有一条 长度为 a 和 b 的最小公倍数的无向边相连。
例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无 向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。
请计算,结点 1 和结点 2021 之间的最短路径长度是多少。
构建mp图
void Init() {
memset(mp, inf, sizeof(mp));//将所有的值定为无穷大
for (int i = 1; i <=n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j)
mp[i][j] = 0;//自己到自己的距离为0
else
if (abs(i - j) <= 21)
mp[i][j] =mp[j][i]= i * j / gcd(i, j);//由题目所述当2个点的编号<=21时,他们之间的距离为这2个编号的最小公倍数。
}
}
}
Dijkstra算法
void dijkstra(int u) {
memset(vis, 0, sizeof(vis));//清零
memset(dis, 0, sizeof(dis));//清零
for (int t = 1; t <= n; t++) {
dis[t] = mp[1][t];//将1到其他点的距离存入dis数组
}
cout << endl;
vis[1] = 1;//1到其他点的距离已用 标为1
for (int t = 1; t <= n; t++) {
int minn = inf, temp=1;
for (int i = 1; i <= n; i++) {
if (vis[i]==0 && dis[i] < minn && dis[i]!=0) {
minn = dis[i];
temp = i;
}//寻找到1点最近的点。
}
vis[temp] = 1;//用过点的标1
for (int i = 1; i <= n; i++) {
if (mp[temp][i] + dis[temp] < dis[i]) {
dis[i] = mp[temp][i] + dis[temp];//只要距离比原来的小就更改
}
}
}
}
完整的代码为:
#include<bits/stdc++.h>
using namespace std;
int n = 2021;
long long mp[2022][2022];
long long vis[2022],dis[2022];//vis判断该点是否已被检索
#define inf 0x3f3f3f3f//inf值给个较大的数
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}//不知道为什么不能直接用_gcd函数,就手写一个
void Init() {
memset(mp, inf, sizeof(mp));
for (int i = 1; i <=n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j)
mp[i][j] = 0;//自己到自己的距离为0
else
if (abs(i - j) <= 21)
mp[i][j] =mp[j][i]= i * j / gcd(i, j); / 由题目所述当2个点的编号 <= 21时,他们之间的距离为这2个编号的最小公倍数。
}
}
}
void dijkstra(int u) {
memset(vis, 0, sizeof(vis));//清零
memset(dis, 0, sizeof(dis));//清零
for (int t = 1; t <= n; t++) {
dis[t] = mp[1][t];
}//将1到其他点的距离存入dis数组
cout << endl;
vis[1] = 1; //1到其他点的距离已用 vis中标为1
for (int t = 1; t <= n; t++) {
int minn = inf, temp=1;
for (int i = 1; i <= n; i++) {
if (vis[i]==0 && dis[i] < minn && dis[i]!=0) {
minn = dis[i];
temp = i;
}//寻找到1点最近的点。
}
vis[temp] = 1;//用过点的标1
for (int i = 1; i <= n; i++) {
if (mp[temp][i] + dis[temp] < dis[i]) {
dis[i] = mp[temp][i] + dis[temp];//只要距离比原来的小就更改
}
}
}
}
int main() {
Init();
dijkstra(n);
cout << dis[n] << endl;
return 0;
}
结果是
改题的答案
yjq