天天看点

【模板】最短路径之Dijkstra算法

是一种贪心的单源最短路径算法(可证明)。

注意点:

1. 图的链式存储(vector数组方式)。

2. 循环n次。可分为两大步骤:加入新的节点后更新最短距离、寻找下一个节点(要有最短距离)并加入集合。

时间复杂度:

【模板】最短路径之Dijkstra算法
//Dijstra算法
#include<cstdio>
#include<vector>
using namespace std;
struct E{
	int next;	//代表直接相邻的顶点
	int c;		//代表该边的权值 
}; 
vector<E> edge[2001];	//邻接链表
bool mark[2001];		//标注为True时表示该结点最短路径长度得到,已加入集合K
int Dis[2001];	//当标注为True时是已得的最短路径长度,
				//否则是从1出发,经过已知的最短路径达到K中的某节点,再经过一条边到达i结点的路径中的最短距离
int main(void)
{
	int n,m;
	while(scanf("%d%d",&n,&m)!=EOF)		//路口数、道路数 
	{
		//if(n==0&&m==0) break;
		for(int i=1;i<=n;i++) edge[i].clear();	//初始化邻接链表
		while(m--)
		{
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			E tmp;
			tmp.c = c;
			tmp.next = a;
			edge[b].push_back(tmp);		//将邻接信息加入邻接链表 
			tmp.next = b;
			edge[a].push_back(tmp);		//再次加入,因为是无向图 
		 } 
		for(int i=1;i<=n;i++)//因为结点从1开始 
		{
			Dis[i]=-1;		//所有结点不可达 
			mark[i]=false; 		//所以结点不属于K 
		}
		Dis[1]=0;	//得到最近的结点为1,长度是0 
		mark[1]=true; 	//将结点1加入K
		int newP = 1;	//集合K新加入的结点是1
		for(int k=1;k<n;k++)	//循环n-1次,按照最短路径递增确定其它n-1个结点的最短路径长度
		{
			for(int j=0;j<edge[newP].size();j++)	//遍历新加入结点的直接相邻的边
			{
				int t = edge[newP][j].next;		//直接相邻的边的另一端 
				int c = edge[newP][j].c;		//直接相邻的边的权重 
				if(mark[t]==true) continue;	//另一端结点如果已经在集合K了,跳过
				if(Dis[t]==-1||Dis[t]>Dis[newP]+c)	//如果该结点尚不可达,或者该结点t从新加入的结点NewP经过该边到达的路径更短
				{
					Dis[t] = Dis[newP]+c;	//更新最短距离信息 
				 } 
			} 
			int min =123123123;//初始化大整数
			for(int j=1;j<=n;j++)	//遍历所有的结点 
			{
				if(mark[j]==true) continue; 	//结点属于K则跳过
				if(Dis[j]==-1)	continue;		//若该结点仍然不可达,也跳过
				if(Dis[j]<min)	//若该结点经由结点至集合K中的某点在经过一条边到达的距离小于当前最小值 
				{
					min = Dis[j];		//更新其为最小值 
					newP = j;		//新加入的结点暂定为这个点 
				 } 
			 } 
			 mark[newP] = true;		//确定为新加入结点 
		 }  
		 printf("%d\n",Dis[n]);
	}
	return 0;
 } 
           

 2019/04/14 第一次错误更正:程序错误(更新的是新加入结点邻接边的另一端结点)。