Dijkstra算法
如有错误请指正!谢谢!
这个算法最通俗的解释就是单源最短路径算法,展开来讲,就是从图中一个点到其他点的最短路径(好像是废话)
Dijkstra算法的核心就是贪心,首先把各个点与起点的距离记录在一个dis数组(起初除了起点,其他的点的最段距离都是不确定的,所以需要一个vis数组来判断这个点是否是距离起点的最短路径点)中,从dis数组中找到最短的那个点,且这个点肯定是目前距离起点最近的(这个想不明白的看看图就知道了),然后以这个点为”中转站“,判断比较从中转站到别的点是否距离更短,如果更短,那就更新dis数组,如此更新(点数-1)次,就能保证所有的点到起点的距离最短。
还是不懂的话,上面几段话多看两遍,然后再结合下面的实例进行相应的理解。
--------------------------------------------------分割线--------------------------------------------------
就以下面的样例作为简单的解释吧
用的是邻接矩阵。
假设一开始起点为1。
用一个map数组存储点与点之间的边的权重,如图所示(手写的有点丑):
那么现在dis数组为0,1,3,∞,∞,∞,vis数组为1,0,0,0,0,0(起点标记为1)
现在距离起点1最近的点就是点2,所以点2就是一个确定的离起点最近的一个点,然后点2就作为中转站,此时还需要更新vis数组(1,1,0,0,0,0)用i从点1开始遍历到点6,比较通过中转站(点2)到达点i的距离是否小于dis中的值,如果小就更新,我们可以看到,从点2可以到点1,点4,点5(因为是无向图所以会有重复,但不影响判断),依次进行比较。
从点1到中转站(点2)到点4,那么权重和就是1+1=2,小于之前的∞,更新dis数组,0,1,3,2,∞,∞
从点1到中转站(点2)到点5,那么权重和就是1+5=6,小与之前的∞,更新dis数组,0,1,3,2,6,∞
点2作为中转站当前能得到的各个点的最短路径已经更新完毕,接下来就是从没有确定最短路径的点(即vis[i]=0)当中取最小的那个点作为中转站(即点4作为中转站),依此类推。
(有个专业名词叫”松弛“,其实就是上面找点作中转站,比较谁更短,然后更新的操作。)
样例输入:
6 8
1 2 1
2 5 5
5 6 2
6 4 9
4 3 7
3 1 3
3 5 10
2 4 1
1
#include<iostream>
#include<string.h>
using namespace std;
#define inf 0x3f3f3f3f
int map[1010][1010];//这是一个二维模拟数组,map[i][j]对应从i到j的边权(注意不要和边长混淆概念)
int vis[1010],dis[1010];//vis[i]代表i到某一点是否是确定的最短路径,dis[i]是确定的最短路径的值
int n,m;//n代表有多少点,m代表有多少边
int start;//代表起点
//本模板点下标从1到n
int main(){
cin>>n>>m;
//初始化很重要,不要忘记
memset(map,inf,sizeof(map));
for(int i=1;i<=n;i++){
map[i][i]=0;
}
//读入图
for(int i=0;i<m;i++){
int u,v,w;
cin>>u>>v>>w;
map[u][v]=w;
map[v][u]=w;
}
/*以下为Dijkstra实现(可抽取为一个函数)*/
//vis数组一开始就是定义在main函数外,所以不需要memset,本身就全是0
//接下来先初始化dis数组
cin>>start;
for(int i=1;i<=n;i++){
dis[i]=map[start][i];
}
vis[start]=1;//因为是初始点,所以直接标记为访问过
for(int i=1;i<n;i++){//为了确保每个点到起始点都是最短路径,就要进行n-1次的迭代,使得vis数组从1到n都是1,即都访问过
//下面的操作是找出未确定最短路径的点中离初始点最近的一个点
int min=inf,index;
for(int j=1;j<=n;j++){
if(!vis[j]&&dis[j]<min){
min=dis[j];
index=j;
}
}
vis[index]=1;//表明已经确定最短路径
//下面的操作就是逐个更新最段路
for(int j=1;j<=n;j++){
if(map[index][j]+dis[index]<dis[j]){
dis[j]=map[index][j]+dis[index];
}
}
}
/*至此,Dijkstra实现结束*/
//这边就进行一下简单的输出
for(int i=1;i<=n;i++){
cout<<dis[i]<<" ";
}
return 0;
}
注释写的好像太多了
样例输出:
0 1 3 2 6 8
标准模板:
#include<iostream>
#include<string.h>
#define inf 0x3f3f3f3f
#define maxn 1010
using namespace std;
int e[maxn][maxn],vis[maxn],dis[maxn];
int n,m,start,dest;
int main(){
//输入,略
memset(e,inf,sizeof(e));
memset(dis,inf,sizeof(dis));
dis[start]=0;
for(int i=0;i<n;i++){
int u=-1,min=inf;
for(int j=0;j<n;j++){
if(!vis[j]&&dis[j]<min){
u=j;
min=dis[j];
}
}
if(u==-1){
break;
}
vis[u]=1;
for(int v=0;v<n;v++){
if(!vis[v]&&e[u][v]!=inf&&dis[u]+e[u][v]<dis[v]){
dis[v]=dis[u]+e[u][v];
}
}
}
return 0;
}
无向图可以,有向图只要保证能到达,也是一样的,区别就在于邻接矩阵的设置。
好了,既然懂了,那不妨写道题目练练手(1003 Emergency)