dijkstra 单源最短路

下面的解析是来自洛谷的题解。 

前言

  • SPFASPFA算法由于它上限 O(NM) = O(VE)O(NM)=O(VE)的时间复杂度,被卡掉的几率很大.在算法竞赛中,我们需要一个更稳定的算法:dijkstradijkstra.

什么是dijkstradijkstra?

  • dijkstradijkstra是一种单源最短路径算法,时间复杂度上限为O(n^2)O(n2)(朴素),在实际应用中较为稳定;;加上堆优化之后更是具有O((n+m)\log_{2}n)O((n+m)log2​n)的时间复杂度,在稠密图中有不俗的表现.

dijkstradijkstra的原理/流程?

  • dijkstradijkstra本质上的思想是贪心,它只适用于不含负权边的图.
  • 我们把点分成两类,一类是已经确定最短路径的点,称为"白点",另一类是未确定最短路径的点,称为"蓝点"
  • dijkstradijkstra的流程如下::
  • 1.1. 初始化dis[start] = 0,dis[start]=0,其余节点的disdis值为无穷大.
  • 2.2. 找一个disdis值最小的蓝点x,x,把节点xx变成白点.
  • 3.3. 遍历xx的所有出边(x,y,z),(x,y,z),若dis[y] > dis[x] + z,dis[y]>dis[x]+z,则令dis[y] = dis[x] + zdis[y]=dis[x]+z
  • 4.4. 重复2,32,3两步,直到所有点都成为白点..
  • 时间复杂度为O(n^2)O(n2)

dijkstradijkstra为什么是正确的

  • 当所有边长都是非负数的时候,全局最小值不可能再被其他节点更新.所以在第22步中找出的蓝点xx必然满足:dis[x]:dis[x]已经是起点到xx的最短路径..我们不断选择全局最小值进行标记和拓展,最终可以得到起点到每个节点的最短路径的长度

图解

  • (令start = 1start=1)
  • 开始时我们把dis[start]dis[start]初始化为00,其余点初始化为infinf 初始化
  • 第一轮循环找到disdis值最小的点11,将11变成白点,对所有与11相连的蓝点的disdis值进行修改,使得dis[2]=2,dis[3]=4,dis[4]=7dis[2]=2,dis[3]=4,dis[4]=7 1
  • 第二轮循环找到disdis值最小的点22,将22变成白点,对所有与22相连的蓝点的disdis值进行修改,使得dis[3]=3,dis[5]=4dis[3]=3,dis[5]=4 2
  • 第三轮循环找到disdis值最小的点33,将33变成白点,对所有与22相连的蓝点的disdis值进行修改,使得dis[4]=4dis[4]=4 3
  • 接下来两轮循环分别将4,54,5设为白点,算法结束,求出所有点的最短路径
  • 时间复杂度O(n^2)O(n2)

为什么dijkstradijkstra不能处理有负权边的情况?

  • 我们来看下面这张图 4
  • 22到33的边权为-4−4,显然从11到33的最短路径为-2−2 (1->2->3).(1−>2−>3).但在循环开始时程序会找到当前disdis值最小的点33,并标记它为白点.
  • 这时的dis[3]=1,dis[3]=1,然而11并不是起点到33的最短路径.因为33已经被标为白点,所以dis[3]dis[3]不会再被修改了.我们在边权存在负数的情况下得到了错误的答案.

dijkstradijkstra的堆优化?

  • 观察dijkstradijkstra的流程,发现步骤22可以优化

  • 怎么优化呢?

  • 我会zkw线段树!我会斐波那契堆!

  • 我会堆!

  • 我们可以用堆对disdis数组进行维护,用O(\log_{2}n)O(log2​n)的时间取出堆顶元素并删除,用O(\log_{2}n)O(log2​n)遍历每条边,总复杂度O((n+m)\log_{2}n)O((n+m)log2​n)

head数组和edge[i]里面的next元素联合用的真是无敌了,可以通过一个循环直接访问到任意一个点周围的所有点。

//我觉的理解这段代码最重要的就是理解head数组和e[]结构体数组里面next元素的结合用法。
//比如我输入1——2 1——3 1——4 1——5
//add_edge里面的处理之后,e[1],e[2],e[3],e[4]里面存储的是这四条边的信息
//最终head[1]=4;表示的是1(最后输入)最后直接相连的边的序号是4
//然后访问e[4],e[4].next会是3,表示比当前边1——4更早相连的边的信息储存在序号为3的结构体数组e中
#include <bits/stdc++.h>
using namespace std;
int N,M,S;
#define inf 0x3f3f3f3f
const int maxn=100005;
int dis[maxn],head[maxn];
int cnt;
int vis[maxn];
struct edge{
	int to,next,dis;
}e[maxn*5];
struct node{
	int dis;
	int pos;
	bool operator<(const node x)const{
		return this->dis>x.dis;
	} 
};

priority_queue<node> q;

void add_edge(int u,int v,int dis)
{
	cnt++;
	e[cnt].to=v;
	e[cnt].dis=dis;
	e[cnt].next=head[u];//这个串联用的真是巧妙
	head[u]=cnt;//这个串联用的真是巧妙
}

void dijkstra()
{
	dis[S]=0;
	node temp;
//	temp.dis=0,temp.pos=S;
//	q.push(temp);
	q.push((node){0,S});
	while(!q.empty())
	{
		temp=q.top();
		q.pop();
		int x=temp.pos;
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=head[x];i;i=e[i].next) 
		{
			int y=e[i].to;
			if(dis[y]>dis[x]+e[i].dis){
				dis[y]=dis[x]+e[i].dis;
				temp.dis=dis[y],temp.pos=y;
				if(!vis[y]) q.push(temp);
			}
		}
	}
}

int main()
{
	cin>>N>>M>>S;
	int i,j,k,u,v,len;
	for(i=0;i<M;i++){
		scanf("%d%d%d",&u,&v,&len);
		add_edge(u,v,len);
	}	
	for(i=0;i<=N;i++)
		dis[i]=inf;
	dijkstra();
	for(i=1;i<=N;i++){
		printf("%d",dis[i]);
		if(i!=N) printf(" ");
		else printf("\n");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41755258/article/details/84593042
今日推荐