1.解决问题
有向加权图的最短路径问题。
2.算法条件
该图所有边的权值非负,即对于每条边(u,v) ∈ E , w(u,v) ≥ 0。
3.模拟算法运行过程
Dijkstra算法中,主要采用了dis[N]数组,记录点到源点的最短距离,以及visit[N]数组,主要用来记录该点是否在集合中(代码中是根据visit[i] == true 或者 visit[i] == false来判断的)。
我多用了一个path[N]数组来记录最短路的路径,初始化为自己的下标。
从1号节点开始,将其相邻的边的权值记录到dis数组中,并且将1号节点的visit状态置为true:
1号节点到2号节点以及4号节点的权值记录在dis数组中,并且对path路径进行刷新path[2]=1,path[4]=1
挑选dis数组里面visit值为false且值最小的点,在上面的状态便是挑选2号节点,因为v1–>v2一定是(v1,v2)的最短路,不可能经过第三个点进行转折,这是因为1号节点到其他点的距离均大于v1–>v2的距离。我们把v2的visit状态置为true:
下面,从2号节点开始,将其相邻的边的权值之和与原值进行对比,取小的,记录到dis数组中,同时更新path数组,并且再次选择dis数组里面visit状态为false且值最小的点。因为(v1–>v2–>v4) =18 > (v1 -->v4) = 10,所以不更新dis[4]和path[4]的值。此次选择的是5号节点,将五号节点的visit值置为true:
下面,从五号节点开始,将其相邻的边的权值之和与原值进行对比,取小的,记录到dis数组中,同时更新path数组,并且再次选择dis数组里面visit状态为false且值最小的点。因为(v1–>v5–>v3) =19 > (v1 -->v2 -->v3) = 17,所以不更新dis[3]和path[3]的值。此次我们选择4号节点,将4号节点visit值置为true:
最后dis数组里面visit值为false且值最小的点就是3号节点,所以在选择3号节点,并将其visit状态置为true:
算法执行结束。
4.算法核心代码:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 1000;
const int INT_MAX = 0x3f3f3f3f;
int e[N][N];//存边,e[i][j] == -1 表示(i,j)之间不存在边
int dis[N];//求最短路的状态数组
bool vis[N];//将已经求得最短路的点状态置为true
int path[N];//用来记录最短路的路径
int n , m; //n --> 点的个数 , m --> 边的个数
//dijkstra算法:start-->最短路起点
void dijkstra(int start){
dis[start] = 0;
int cnt = 0;
path[1] = 0;
while(cnt < n){
int temp = INT_MAX;
int tempIndex;
for(int i = 1;i <= n;i++){
if(!vis[i] && temp > dis[i]){
temp = dis[i];
tempIndex = i;
}
}
vis[tempIndex] = true;
cnt++;
//更新tempIndex到其他未访问的点的最短路径
for(int i = 1;i <= n;i++){
if(!vis[i] && e[tempIndex][i] != -1 && dis[i] > dis[tempIndex] + e[tempIndex][i]){
dis[i] = dis[tempIndex] + e[tempIndex][i];
path[i] = tempIndex;
}
}
}
}
int main(){
memset(e , -1 , sizeof(e));
memset(dis , 0x3f , sizeof(dis));
memset(vis , false , sizeof(vis));
int from , to , cost;
cin >> n >> m;
for(int i = 0;i < m;i++){
cin >> from >> to >> cost;
e[from][to] = cost;
}
dijkstra(1);
for(int i = 1;i <= n;i++){
cout << dis[i] << " " ;
}
cout << endl;
return 0;
}