最短路——迪杰斯特拉算法

1.在图论中经常会碰到最短路问题。最短路顾名思义就是在有向连通图(也就是连通网)中寻找两点之间的最短距离。为了解决这个问题人们提出了很多行之有效的算法,这其中有著名的五大算法:Dijkstra算法,SPFA算法,Floyd算法,Bellman-Ford算法,以及基于启发式搜索的A*算法。我们接下来会学习每一个算法,首先是DJ算法。

2.DJ算法是一种求解单源最短路非负权值的有效方法。基本思想:首先,建立两个集合,以集合中的每一个点作为研究对象。第一个集合我们叫做S,第二个叫做D。初始化的时候,S内放我们的源点,D内放除源点之外的其他点。(what?什么叫源点?源点就是起点呗,这里的源有源头的意思,这个定义在网络流的中会更加贴切,我们这里暂时把它理解为起点)。我们再来看一下我们的问题,我们需要找一个点到另一个点的最短路。所以S内起始只有一个元素,D内有n-1个元素(假设有n个点)。初始化完成以后,我们开始做事情。分为三步:

(1)首先,我们在D中扫描,找出D中的每一个点到源点的距离,如果二者没有直接相连的边,这个距离被设置成INF。所以这里我们就需要一个二维数组保存每两个连通点之间的距离,还要有一个数组,来标记这个点已经被访问过。

(2)扫描之后,找到了距离源点最近的点,将该点加入S中。

(3)重点来了!我们先给出定义,下边将详细讲解这个地方,这也是理解DJ的关键所在。继续执行(1)(2),这个时候,在D中寻找距离第二个点最近的点,并使用第二个点更新距离数组。反复执行,直到D为空集。

我们来理解一下3,上一张

其实第三步是著名的松弛技术:基于三角不等式来的。if : dis[j]>dis]i]+map[s][j],可以将dis[j]更新。我们对着上边的图解释一下:

起始时:A到B,C,D的距离是:10,INF,20.注意我们要寻找的是A到C的最短路径。所以我们的dis数组执行完以后就会被更新成这样的情况:dis[B]=10,dis[C]=INF,dis[D]=20.这个时候,我们将B加入集合S。最最精华的部分来了:现在B加入以后,我们继续更新dis数组,我们发现,A虽然不能直接到C,但是C可以从B来,也就是上边的不等式。我们发现dis[C]>dis[B]+map[B][C],OK,那么我们就更新dis[C],这样就达到了一种松弛的效果。继续做这些操作,每一个点的任务都是用来松弛源点到终点的距离,这样我们通过一种贪心的思想就可以达到最优的效果。下边给出HDU2544最短路裸题的代码:

#include"stdafx.h"
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<stdio.h>
using namespace std;
#define N 110
#define MAX 999999
int nodenum, edgenum;
int map[N][N], dis[N];
bool visit[N];
//最短路的DJ算法
int Dijkstra(int src, int des)
{
	int temp, k;
	//初始化visit数组
	for (int i = 0; i < sizeof(visit); i++)
	{
		visit[i] = false;
	}
	//初始化边集
	for (int i = 1; i <= nodenum; ++i)
		dis[i] = (i == src ? 0 : map[src][i]);
	visit[src] = true;
	dis[src] = 0;
	//贪心寻找最短路,并且通过该点松弛其他边
	for (int i = 1; i <= nodenum; ++i)
	{
		temp = MAX;
		for (int j = 1; j <= nodenum; ++j)
			if (!visit[j] && temp > dis[j])
				temp = dis[k = j];
		if (temp == MAX)
			break;
		visit[k] = true;
		for (int j = 1; j <= nodenum; ++j)
			if (!visit[j] && dis[j] > dis[k] + map[k][j])
				dis[j] = dis[k] + map[k][j];
	}
	return dis[des];
}

int main()
{
	int start, end, cost;
	int answer;
	while (~scanf("%d%d", &nodenum, &edgenum) && (nodenum + edgenum))
	{
		for (int i = 1; i <= nodenum; ++i)
			for (int j = 1; j <= nodenum; ++j)
				map[i][j] = MAX;
		for (int i = 1; i <= edgenum; ++i)
		{
			scanf("%d%d%d", &start, &end, &cost);
			if (cost < map[start][end])
				map[start][end] = map[end][start] = cost;
		}
		answer = Dijkstra(1, nodenum);//源点到终点的最短路
		printf("%d\n", answer);
	}
	return 0;
}


猜你喜欢

转载自blog.csdn.net/weixin_41863129/article/details/83649902