最短路径【迪杰斯特拉dijkstra】

【最短问题】

最短路径问题:如果从图中某一顶点(称为源点)到达另一顶点(称为终点)的路径可能不止一条,如何找到一条路径,使得沿此路径各边上的权值总和达到最小。(权值非负)

                     

【Dijkstra思想】

按路径长度的递增次序,逐步产生最短路径的算法。首先求出长度最短的一条最短路径(顶点0到其他顶点的直接路径最短的路径),再参照它求出长度次短的一条最短路径,依次类推,直到从顶点v到其它各顶点的最短路径全部求出为止。

【步骤】

注意:

V0到T中顶点Vk的最短路径,要么是从v0到vk的直接路径要么是从v0经S[Ii]=1中某个顶点 vi  再到vk的路径

需要:

dist[n]:dist[i]表示当前找到的从源点v0到终点vi的最短路径的长度,初始状态下,dist[i]为E[v0][i],即邻接矩阵的第v0行。

S[n]:为0表示vi还未加入到集合S中,为1表示vi已加入到集合S中。初始状态下,S[v0]为1,其余为0,最初只有顶点v0。

path[n]:p[i]表示在最短路径上顶点vi的前一个顶点号,p[i]=k。从k到i。

过程:

①在dist[ ]里查找S[i]!=1,并且dist[i]最小的顶点u

②将S[u]改为1,表示顶点u已经加入进来了。

③修改其它顶点的dist[ ]及path[ ]。当S[k]!=1,且顶点vu到顶点vk有边(E[u][k]<MAX),且dist[u]+map[u][k]<dist[k],则修改dist[k]为dist[u]+E[u][k],修改path[k]为u。

现在解释一下从初始状态到(1):初始状态。要从未加入S集合的dist[]开始找,可知dist[2]=5最小,记下u=2,那么把点2加入S集合,S[2]=1,接着修改dist[i]和path[i],dist[i]=min{ dist[i],dist[u] + map[2][i] }={0,20,5,30,∞,12},20是因为0到2为5,2到1为15,所以0到1为20,比原来∞小,所以用20去更新。以此类推。

可知 第一条 最短路径为 0->2  第二条为0->2->5.

【应用】

求从源点0到终点4的最短路径和最短路径长度。

path[4]=1→path[1]=2→path[2]=0       dist[4]=28           

最短路径为0,2,1,4,最短路径长度为dist[4]=28。

【模板代码】

#include <stdio.h>
#include <string.h>
using namespace std;
#define INF	0x3f3f3f3f	//无穷大
#define MAXN 20		//顶点个数的最大值

int n;				//顶点个数
int Edge[MAXN][MAXN];	//邻接矩阵
int S[MAXN];		//Dijkstra算法用到的3个数组
int dist[MAXN];		//
int path[MAXN];		//
//求顶点v0到其他顶点的最短路径
void Dijkstra( int v0 )	
{
	for( int i=0; i<n; i++ )    // 初始化三个数组
	{
		dist[i] = Edge[v0][i];  
		S[i] = 0;
		if( i!=v0 && dist[i]<INF )  
			path[i] = v0;
		else 
			path[i] = -1;
	}
	//顶点v0加入到顶点集合S,然后v0到v0的距离为0.
	S[v0] = 1;  
	dist[v0] = 0;

	for( int  i=0; i<n-1; i++ )		//从顶点v0确定n-1条最短路径
	{
		int min = INF;
		int u = v0;
		
		//选择当前未加入集合S中具有最短路径的顶点u
		for(int  j=0; j<n; j++ )
		{
			if( !S[j] && dist[j]<min )
			{
				u=j;  
				min = dist[j];         //最短路径记为min
			}
		}
		S[u] = 1;	//将顶点u加入到集合S,表示它的最短路径已求得
		for( int k=0; k<n; k++ )	//修改dist和path数组元素值
		{
			//未加入集合S中的顶点  // Edge[u][k]<INF代表点u可直达k //经过这个点从0到终点的距离小于原来不经过这个点到终点距离
			if( !S[k]  &&  Edge[u][k]<INF  &&  dist[u] + Edge[u][k] < dist[k] ) 
			{
				dist[k] = dist[u] + Edge[u][k];  path[k] = u;
			}
		}
	}
}

int main( )
{
	int u, v, w;	//边的起点和终点及权值
	scanf( "%d", &n );	//读入顶点个数n
	while (1)
	{
		scanf("%d%d%d", &u, &v, &w);	//读入边的起点和终点
		if (u == -1 && v == -1 && w == -1)
			break;
		Edge[u][v] = w;	//构造邻接矩阵
	}
	for( int i=0; i<n; i++ )
	{
		for( int j=0; j<n; j++ )
		{
			if( i==j ) Edge[i][j] = 0;    //自己到自己为0
			else if( Edge[i][j]==0 )      //不存在i直接到j 
				Edge[i][j] = INF;
		}
	}
	Dijkstra( 0 );	//求顶点0到其他顶点的最短路径
	int shortest[MAXN];	//存放最短路径上的各个顶点的序号
	for( int i=1; i<n; i++ )
	{
		printf( "%d\t", dist[i] );	//输出顶点0到顶点i的最短路径长度
		 //以下代码用于输出顶点0到顶点i的最短路径
		memset( shortest, 0, sizeof(shortest) );
		int k = 0;	             //k表示shortest数组中最后一个元素的下标
		shortest[k] = i;          //表示从i要到0.
		while( path[ shortest[k] ] != 0 )      //path[i]!=0代表i前面还有点。直到它前面这个点的path为0
		{
			k++;
			shortest[k] = path[ shortest[k-1] ];
		}
		k++; 
		shortest[k] = 0;   //最后把0补上   
		for( int j=k; j>0; j-- )
			printf( "%d→", shortest[j] );
		printf( "%d\n", shortest[0] );
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_40317006/article/details/81411553