⭐⭐⭐适用范围:求有权图的最短路径
(边权要是正数).
来个例题:
给定一个n个点m条边的有向图,图中可能存在重边和自环,所有边权均为正值。
请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出-1。
输入格式:
第一行包含整数n和 m。
接下来 m行每行包含三个整数 x,y,z,表示存在一条从点x 到点 y的有向边,边长为z。
输出格式 :
输出一个整数,表示1号点到n号点的最短距离。
如果路径不存在,则输出-1。视频讲解数据范围1≤n≤500,1≤ m≤105,图中涉及边长均不超过10000。
样例:
3 3
1 2 2
2 3 1
1 3 4
输出 :
3
先看下纯享代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std
const int N = 510;
int n ,m;
int q[N][N];
int dist[N];
bool st[N];
int dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[1] = 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;
st[t] = true;
for(int j = 1;j <= n;j ++)
dist[j]= min(dist[j],dist[t] + q[t][j]);
}
if(dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main()
{
cin >> n >> m;
memset (q,0x3f,sizeof q);
for(int i = 0;i < m;i ++)
{
int a,b,c;
cin >> a >> b >>c;
q[a][b] = min(q[a][b],c);
}
int t = dijkstra();
cout << t << endl;
return 0;
}
ok接下来解释下以及说一下自己的见解 ୧⍢⃝୨
主体思路:
①:是用邻接矩阵去存一下每个边权
…
就比如
1 2 2
表示 1到2的边权是2.
for(int i = 0;i < m;i ++)
{ int a,b,c;
cin >> a >> b >>c;
q[a][b] = min(q[a][b],c);
}
在这段代码里就用 q[1][2] = 2 来储存
(题目里说会有重边,就是一个边会有多个边 权我们取最小的,而自环我们这个可以直接规避就是说不用管了。
对于重边,就比如它还有 1 2 5 什么之类的这时候那个min函数(因为只求最短路径所以是min函数)的作用就体现出来了
)
如果没有那么那个距离就是无穷(用0x3f3f3f3f来指带的)上面是用
memset 来直接初始化各个权边的
②:在dijkstra函数里面实现距离的累积
用dist数组来存起点到每个点的最小距离,走到最后一个点时也就是来存我们的答案,是靠一步一步叠加的.
int dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[1] = 0;
先把距离给初始化,先是设置为无穷(和上面一样套路)但是要把起点给设置出来
dist[1] = 0;就是1到1的最短距离为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;
st[t] = true;
for(int j = 1;j <= n;j ++)
dist[j]= min(dist[j],dist[t] + q[t][j]);
}
if(dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
可以看到有三个for循环
首先第一个for循环仅仅代表
对于第二个for循环,是确定下一个最近的那个点也就是q[a][b]的a的值
if(!st[j]&&(t == -1||dist[t] > dist[j]))
看到此处的条件首先
(条件0&&(条件1||条件2))
只要条件0和后面无论是条件1还是2满足还是同时满足就执行if语句
也就是
0+1
0+2
0+1+2
形式都行
然后其中
用bool类型的st来表示有没有遍历过因为初始都是false嘛所以用 !st[j] .
对于(t == -1)表示是第一个进来的距离
对于(dist[t] > dist[j])
来说就是看那个最小那个就是那个下一个距离点
最后一个for是确定那个距离的
其实这三个是紧密联系的要一起看
举个例子
第一次i = 0
然后进入里面的第一个for循环
然后第一次进入时,就相当于是从一个虚无走向1这个点,后面的那个(dist[t] > dist[j])条件第一次是用不到的毕竟初始值都是无穷嘛。
也就是那个q[a][b]里面的a是t
再把它变为true表示已经确定过的点
然后在进行下一步的for循环,确定从t这个点出发到所有点的距离
for(int j = 1;j <= n;j ++)
dist[j]= min(dist[j],dist[t] + q[t][j]);
可以看到就是遍历从开头到每一个点的距离最小值
箭头上的数字表示距离
就是说
dist[1] = 0
dist[2] = 2
dist[3] = 无穷
dist[4] = 2
dist[5] = 无穷
可以看到当走不到的时候我们是用无穷来表示
毕竟初始值我们是设置无穷嘛。
然后进入下一个大循环 i = 1
在进入里面的第一个for循环
int t = -1;
for(int j = 1;j<= n;j ++)
if(!st[j]&&(t == -1||dist[t] > dist[j]))
t = j;
以下为对此代码的实例分析:
j = 1时 st[1] = true已经确定了所以不满足if的条件,j = 2
对照上面的dist[2] = 2
然后t就暂时 t = j = 2
因为后面还有没有遍历完的 3-4-5
所以会继续遍历, j=3时因为dist[3] = 无穷
在和dist[t] = 2比较显然不满足dist[t] > dist[j]
也不满足 t = -1,然后进入下一个 j=4
可以看到dist[4] = 1
满足dist[t] > dist[j]就会改变t = 4也就是把
q[a][b]里面的a = 4也就是第二次是从4这个
点来出发.
当然j=5显然也是不满足的我们直接跳过
进入下一个环节 先把它标记为st[4]=true
进入里面的第二个for循环
for(int j = 1;j <= n;j ++)
dist[j]= min(dist[j],dist[t] + q[t][j]);
和上面一样
因为它下一个啥都没有所以都是无穷
(深入思考:这里自己也是无穷是因为看q[a][b]的实际q[4][4]就是无穷,而一开始的是我们自己设置的dist[1]=0 dist[j]=min(dist[j],dist[t] + q[t][j]);
可以看到虽然q[1][1]是无穷但是我们有min函数所以它会让它等于0)
然后这里有个min函数就是说
要和上一个
dist[1] = 0
dist[2] = 2
dist[3] = 无穷
dist[4] = 1
dist[5] = 无穷
与累加后的值进行比较
q[4][1]+dist[1] = 无穷
q[4][2]+dist[1] = 无穷
q[4][3]+dist[1] = 无穷
q[4][4]+dist[1] = 无穷
q[4][5]+dist[1] = 无穷
然后在取最小的那个更新
更新为:
dist[1] = 0
dist[2] = 2
dist[3] = 无穷
dist[4] = 1
dist[5] = 无穷
然后进入下一个大循环i = 2
和上面分析的一样
j =1和4已经被标记了所以就不会在进去
其实到这里我们就可以看出来
这第一个for循环就是来确定下一个被上一个点指向点的先后顺序(有点点绕,在这个例子里就是说1指向2和4然后判断是先从4开始还是从2开始我们这是小一点的先开始)
然后按照上面逻辑t的下一个值就是t=2
先标记st[2] = true
进入里面第二个for循环
同上面的逻辑
更新每个dist(这里直接给更新后的数据)
dist[1] = 0
dist[2] = 2
dist[3] = 3
dist[4] = 1
dist[5] = 无穷
(到这里我们可以恍然大悟了原来第二个for循环就是来确定每一次从1到这个点的最短距离)
然后进入i = 3循环
到这里应该比较清晰了
第一次的for循环t = j = 3嘛在标记下
第二次的for
我们直接更新数据
dist[1] = 0
dist[2] = 2
dist[3] = 3
dist[4] = 1
dist[5] = 4
此时i=3嘛这里就可以知道为什么是i<n-1
解释:
(我们这个例子n是5,就是说 不满足i<4就会退出
循环,然后我们的i下一步进入for循环刚好是i = 4就是说,会退出,
我们五点,用了四次大的for循环就可以出答案,其实这里我们也可以是i = 1;i<n
因为第一个点我们是直接设置了就为0所以是可以少一次)
出了循环体
然后最后一步判断
if(dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
就是说如果还是无穷(用0x3f3f3f3f来代替的)那么说明不能走到,不然就是走到了
这里用不用else都行不用是为了少打几个字符
然后为什么是朴素版嘞,(你可以看到这里的里面的第二个for循环每次都是要遍历n次但是实际又不需要n次会浪费许多不必要的时间)
ok看到这里我们就把这个算法学完啦!!!
ε٩(๑> ₃ <)۶з
制作不易,给个免费的赞呗꒰ↀωↀ꒱❤❤