1.定义
迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。
*以上摘自百度百科
要知道的点是:所有边上的权值必须是非负值,否则不能用这个算法。
2.逻辑
1.我们先默认所有点之间都没有联系,所以长度设置为无穷大。
2.输入起点from和终点to
3.把起点的可达点加入队列
4.遍历这些点的距离,挑选出权值最短的,把它前往的那个点状态设置为访问过。
5.算出其它方案到这个点是否比前一个方案耗费权重更少,如果更少就更新权重。
6.重复3~5步,直到全部的点都访问过。
3.实现方法
先上个图!(大家可以根据这个图来自己根据逻辑模拟一遍,这样就更能理解下面的代码)
熟悉的G1来作为示范吧。(测试数据会用到)
首先,我们还是需要设置一个“无穷大”的值:
#define INF 0x3f3f3f
//或者如下
const int INF=0x3f3f3f;
然后我们使用数组来存储起点到每个点的最短距离,用bool数组存储是否访问过,并使用二维数组来存储邻接矩阵:
const int maxn=1001;//设置一个常量最大值
bool vis[maxn];//访问过?
int dis[maxn];//最短距离
int adj[maxn][maxn];//邻接矩阵
之后,再定义点数、边数、起点和终点的变量即可:
int n,m;//点、边的数量
int from,to;//起终点
好了,变量定义的部分我们就完成了。接下来看一下主函数部分:
(这个主函数很简单,所以直接在代码里加注释加以说明):
int main()
{
cin>>n>>m;//点的数量和边得数量
for(int i=0;i<=m;i++)
{
int u,v,w;//两个顶点和权值
adj[u][v]=adj[v][w]=w;//邻接矩阵
}
cin>>from>>to;//起终点
dijkstra();//迪杰斯特拉算法
cout<<dis[to];//输出
return 0;//结束程序
}
好了,到最核心的部分了,也就是迪杰斯特拉算法函数内部。
首先,我们先定义两个变量:minn表示从起点开始最小的权值,pos表示那个minn所对应的那个边权值最小的点的下标。
int minn;//起点开始最小的权值
int pos;//所对应的那个边权值最小的点的下标
之后进行初始化:先全部设置为没有被访问过,把距离通过邻接矩阵更新起点开始的距离,并把起点设置为访问过。
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++){
dis[i]=adj[from][i];
}
vis[from]=true;
接下来就是把n个顶点所要的权值进行比较,最小的权值设置为minn(每次循环都要初始化为无穷大),角标设置为pos,每次更新最小值:
注:以下本函数内部的操作均在这个for(int i=1;i<=n;i++)这个循环内部进行操作。
for(int i=1;i<=n;i++)
{
minn=INF;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&dis[j]<minn)
{
minn=dis[j];
pos=j;
}
}
}
筛选完后,把该点设置为访问过。
vis[minn]=true;
接下来就是循环更新最短路径了。
对于没有被访问过的点,如果发现有别的方法去这个点的路径权重比原来的路径小,就更新起点到该点的最短距离。
for(int j=1;j<=n;j++)
{
if(!vis[j]&&dis[j]>dis[pos]+adj[pos][j])
{
dis[j]=dis[pos]+adj[pos][j];
}
}
到了这里,整个迪杰斯特拉算法的函数也就算完成了,只需把这个函数放在程序中即可。
还是老套路,献上完整代码:
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f//“无穷大”的值
//或 const int INF=0x3f3f3f;
const int maxn=1001;//设置一个常量最大值
bool vis[maxn];//访问过?
int dis[maxn];//最短距离
int adj[maxn][maxn];//邻接矩阵
int n,m;//点和边的数量
int from,to;//起终点
void dijkstra();//迪杰斯特拉算法函数
int main()
{
cin>>n>>m;//点的数量和边的数量
for(int i=0;i<m;i++)
{
int u,v,w;//两个顶点和权值
cin>>u>>v>>w;
adj[u][v]=adj[v][w]=w;//邻接矩阵
}
cin>>from>>to;//起终点
dijkstra();//迪杰斯特拉算法
cout<<dis[to];//输出结果
return 0;//结束程序
}
void dijkstra()
{
int minn;//最小权重值
int pos;//最小权重的角标
memset(vis,false,sizeof(vis));//初始化为没有访问过
for(int i=1;i<=n;i++)//初始化
{
dis[i]=adj[from][i];
}
vis[from]=true;//起点设为访问过
for(int i=1;i<=n;i++)//找最小权重部分
{
minn=INF;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&dis[j]<minn)
{
minn=dis[j];
pos=j;
}
}
vis[minn]=true;//设为访问过
for(int j=1;j<=n;j++)//在没有访问过里面更新最短路径
{
if(!vis[j]&&dis[j]>dis[pos]+adj[pos][j])
{
dis[j]=dis[pos]+adj[pos][j];
}
}
}
}
G1测试数据 :
4 5
1 2 1
1 3 4
1 4 8
2 3 6
2 4 4
1 4
输出结果:
结束~