最短路问题的朴素版Dijkstra算法
- 题目
最短路问题需要用到下面的算法(n代表点的数量,m代表边的数量)
朴素版和堆优化版的Dijkstra算法的区别是,朴素版比较适合稠密图,堆优化版适合稀疏图,稠密图代表它的边比较多,边的数量最多是点的平方,稀疏图代表边基本于点的数量差不多。
对于下面的这个题,就需要用到朴素版的Dijkstra算法。
题目
给定一个 n n n 个点 m m m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。
请你求出 1 1 1 号点到 n n n 号点的最短距离,如果无法从 1 1 1 号点走到 n n n 号点,则输出 − 1 -1 −1。
输入格式
第一行包含整数 n n n 和 m m m。
接下来 m m m 行每行包含三个整数 x , y , z x,y,z x,y,z,表示存在一条从点 x x x 到点 y y y 的有向边,边长为 z z z。
输出格式
输出一个整数,表示 1 1 1 号点到 n n n 号点的最短距离。
如果路径不存在,则输出 − 1 -1 −1。
数据范围
1 ≤ n ≤ 500 1 \le n \le 500 1≤n≤500,
1 ≤ m ≤ 1 0 5 1 \le m \le 10^5 1≤m≤105,
图中涉及边长均不超过10000。
输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3
朴素版的Dijkstra思路如下:
- 将所有点到1号点的距离设置为正无穷(一个很大的数,当做正无穷看待,并不是真正的正无穷),将1号点的距离设置成0。
- 首先外层遍历 n 次循环
- 每次循环找到一个集合外的点当中距离离1最近的点,假设是 t
- 把该点放到集合当中
- 利用t更新 与 t 连接的点
存储稠密图需要用到邻接矩阵存储,g[a] [b] 代表 a点 到 b 点的距离。
数组 dist 用来存储n号点到 1号点的距离,st 代表该点是在集合内还是集合外。
一般建议每次将题目的点的数量写成N,将边写成 M,并且尽量在原有最大数据上 加一点数字,我这里都多了10,是为了防止出现数组不够的情况
首先是 输入环节,开始将整个图存起来
由于题目当中会含有重边,所以我们需要 2 号语句,只算最小的那条边,由于这样最开始的 二维数组未初始化,所以值都是0,所以我们需要用 1 号语句,将所有值变为 正无穷大,这样就不会求最小值是0了,而且写1号语句的也不仅如此,还关系到点的更新,所以必须进行初始化
我们可以用 0x3f3f3f3f 当做无穷大,这个数作为无穷大的好处是 乘以2之后不会溢出 int 的范围,并且该数字很大。
memset 里面的是 0x3f 的原因是 memset 设置的是每个字节的值,所以每个字节是 0x3f,int 类型是4个字节,所以就是 0x3f3f3f3f。
dijkstra函数执行完之后,数组 dist[ n ]存的就是 1号点到 n 号点的距离。
在dijkstra 函数内部我们会将 dist数组当中所有的值设置成 正无穷大,也就是0x3f3f3f3f,所以如果当dist[n]还是正无穷大时,也就证明1号点走不到 n 号点,没有点更新 n号点。
根据题目要求走不到则打印 -1
在dijkstra函数中,就需要用到刚才的思路。
第一步,将数组 dist 中的值 设置为正无穷大(跟刚才设置数组g 一样),将1号点的距离设为1。
接着外层for循环 到 n
在循环内部有 三个步骤。
-
每次找到所有集合外的点当中距离最近的点
变量 t 为每次最近的点,遍历一遍所有的点,!st[j] 代表在集合外,如果当 t 还是 -1 或 当 t 这个点的距离遇到更小的距离的时候,则将 j 赋值给 t。此时 t 点就是距离最近的点
-
第二步 将该点放到集合当中,代表着它的距离已经确定了。
数组 st 的含义就是代表该点是否在集合里,所以只需要将 t 点设为 true 即可
- 第三步,利用 t 点更新其他可以更新的点
这里也解释了为什么 数组 g 要设置成正无穷大,正无穷大的含义相当于 t 点 与 j 点没有直接练在一起。
所以如果没有连在一起,那么也就不会更新。
也就是说,这一步,遍历了所有的点,看一看能不能 用 t 更新它,如果 1号点到 t点 的距离加上 t到 j 的距离 小于1号点到 j点的距离,那么就会更新。
比如这里原来 j 号点到 1 的距离是 2 + 4 = 6,此时 t 到1的距离为3,加上 t 到 j 的距离 1 为 4,因为小于6,所以就会更新 j 到 1 的距离。
最后还可以进行一点小小的优化,不优化也没任何问题
更新 n - 1 次的时候, n 号点 就必定会被更新,当然前提能到达的情况下,因为每次都找一个集合外最近的点。
当找到 最近的点为 n 时,说明此时 n 号点的距离就已经确定了,所以可以直接break出去。
完整代码如下:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>using namespace std;const int N = 510, M = 1e5+10;int g[N][N];
int dist[N];
bool st[N];int n, m;void dijkstra()
{memset(dist, 0x3f, sizeof dist);dist[1] = 0;for (int i = 0; i < n - 1; i++){//1.找到距离最近的点int t = -1;for (int j = 1; j <= n; j++)if (!st[j] && (t == -1 || dist[t] > dist[j]))t = j;if (t == n) break;//2.将距离最近的点放到集合当中st[t] = true;//3.更新点for (int j = 1; j <= n; j++)if (!st[j])dist[j] = min(dist[j], dist[t] + g[t][j]);}
}int main()
{scanf("%d%d", &n, &m);memset(g, 0x3f, sizeof g);//1for (int i = 0; i < m; i++){int a, b, c;scanf("%d%d%d", &a, &b, &c);g[a][b] = min(g[a][b], c);//2}dijkstra();if (dist[n] == 0x3f3f3f3f) puts("-1");else printf("%d\n", dist[n]);return 0;
}
完