适用条件
图中没有负权边的最短路径问题
主要思想
通过迪杰斯特拉算法计算图G中的最短路径时,需要指定起点s。
此外,需要引进两个集合S和U。S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离)。
初始时,S中只有起点s;U中是除s之外的顶点,并且U中顶点的路径是“起点s到该顶点的路径”。然后,从U中找到路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。然后,再从U中找到路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。……重复该操作,直到遍历完所有顶点。
① 初始时, S只包含起点s;U包含除s之外的其他顶点,且U中顶点的距离为“起点s到该顶点的距离”【例如:U中顶点v的距离为(s, v)的长度,然后s和v不相邻,则v的距离为∞】。
②从U中选出“距离最短的顶点k”,并将顶点k加入到S中;同时,从U中移除顶点k。
③更新U中各个顶点到起点s的距离。之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其他顶点的距离;例如,(s, v)的距离可能大于(s, k)+(k, v)的距离。
④重复步骤2和3,直到遍历完所有顶点。
参考链接
代码实现
#include <iostream>
#include <cstring>
#include <cstdio>
/*问题描述:
* 输入n和m,代表n个节点,m条边,然后是m行输入,每行有x,y,z,代表x到y的路距离为z。
* 问题:从1出发到各点的最短路径。
* 测试样例:
7 12
1 2 20
1 3 50
1 4 30
2 3 25
2 6 70
3 4 40
3 6 50
3 5 25
4 5 55
5 6 10
5 7 70
6 7 50
*/
using namespace std;
const int maxn = 100;
int map[maxn][maxn];
int dis[maxn];
int path[maxn];
int vis[maxn];//记录更新过的点
int n;
void dijk(int s)
{
//初始化
memset(path,-1,sizeof(path));
/*INF使用0x3f3f3f3f的好处:
* 1:满足无穷大加一个有穷的数依然是无穷大(在DijKstra算法松弛操作中避免了溢出而出现负数)
* 2:满足无穷大加无穷大依然是无穷大(两个0x3f3f3f3f相加并未溢出)
* 3:初始化时,由于每一个字节为0x3f,所以只需要memset(buf,0x3f,sizeof(buf))即可
*/
memset(dis,0x3f,sizeof(dis)); //初始化为无穷大
memset(vis,0,sizeof(vis));
dis[s] = 0; //自身到自身的距离为0
while(1)
{
int k = 0;
for(int j = 1; j <= n; j++)
{
if(!vis[j]&&dis[j]<dis[k])//找未收录顶点中dis值最小的
k=j; //这里第一次找到的是起点
}
if(!k) return; //没有未收录的点,则返回
vis[k] = 1;
//松弛操作
for(int j = 1; j <= n; j++)
{
//第一次循环只有起点的邻接点距离被更新,每次都更新新找到的点的邻接点
if(dis[j]>dis[k]+map[k][j])
{
dis[j]=dis[k]+map[k][j];
path[j]=k;//路径被改变,重新记录前驱,最短路是由最短路+某一条固定路组成,所以前驱是有效的
}
}
}
}
void print(int x)//x为终点
{
if(x == -1) return;
//递归
print(path[x]);
printf("%d->",x);
}
int main()
{
int m,x,y,z,order;
scanf("%d%d",&n,&m);
memset(map,0x3f,sizeof(map));
for(int i = 0; i < m; i++)
{
scanf("%d%d%d",&x,&y,&z);
map[x][y] = z;
}
dijk(1);
scanf("%d",&order);//order为终点
print(path[order]);
printf("%d\n",order);
//打印最短距离
printf("%d\n",dis[order]);
return 0;
}
参考链接