Dijkstra算法

单源最短路问题

给定一张 (n) 个点的有向图,边权均为正数。给定起点 (st) ,要求出 (st) 到其他所有点的最短路。

算法简介

( ext{Dijkstra}) 算法是典型的单源最短路径算法,朴素算法的时间复杂度为 (O(n^2)) ,加上堆优化后可以达到 (O((n+m)log n)) 的时间复杂度,并且在实际应用中较为稳定,需要在各种题目中灵活应用!

算法流程

( ext{Dijkstra}) 算法的主要思想是贪心,但它只适用于不含负权边的图。

它的主要思想是:以起始点为中心向外层层扩展,直到扩展到终点为止。

  1. 初始化

(d[i]) 表示起点 (st)(i) 点的最短距离,并把初始值为无穷大 (+∞)(d[st]=0) 。把点集 (V) 分成两部分:已经确定最短路的点集 (S) 与未确定最短路的点集。我们先将 (S) 初始化为空集。

  1. 具体过程

先找到一个不在 (S)(d) 值最小的点 (u) ,然后将它加入到 (S) 中。

再扫描节点 (u) 所有出边 ((u,v,w)) ,对所有 (v) 做一次松弛操作,即若 (d[v]>d[u]+w) ,则用 (d[u]+w) 更新(d[v])

重复上述步骤,直到所有节点被加入到 (S) 中。

正确性的简要证明

我们知道,当所有边长都是非负数的时候,全局最小值不可能再被其他节点更新。即在第 (2) 步中找出的 (u) 一定不会存在其它的点再来更新 (d[u]) 此时的 (d[u]) 已经是从起点到 (u) 的最短路径。我们不断选择确定为最小值的点进行标记和拓展,最终就可以得到起点到每个节点的最短路径。

朴素算法

void dijkstra()
{
	bool vis[N];
	memset(d,0x3f,sizeof d);
	memset(vis,0,sizeof vis);
	d[st]=0;
	while(1)
	{
		int u=-1;
		for(Re int i=1;i<=n;i++)
		{
			if(!vis[i]&&(u==-1||d[i]<d[u]))
			{
				u=i;
			}
		}
		if(u==-1) break;
		vis[u]=1;
		for(Re int i=0;i<g[u].size();i++)
		{
			int to=g[u][i].id;
			int Len=g[u][i].d;
			if(d[to]>d[u]+Len)
			{
				d[to]=d[u]+Len;
			}
		}
	}
}

算法优化

然而,我们发现该程序的时间复杂度虽然只有 (O(n^2)) ,但仍然无法满足大多数题目的要求。

于是,我们便考虑用一些操作来优化上述步骤。

优先队列 ( priority_queue )

这就是所谓的堆优化 ( ext{Dijkstra}) ,是较为常用的优化方式。

我们考虑用堆对 (d) 数组进行维护,共进行的 (n) 次操作,每次用 (O(log n)) 的时间取出堆顶元素并删除;共用 (O(mlog n)) 遍历每条边。故总时间复杂度为 (O((n+m)log n))

struct Node {
	int id,d;
	bool operator < (const Node &a) const{
		return d>a.d;
	}
};

void dijkstra()
{
	bool vis[N];
	memset(d,0x3f,sizeof d);
	memset(vis,0,sizeof vis);
	d[st]=0;
	priority_queue<Node> q;
	q.push((Node){st,0});
	while(!q.empty())
	{
		int u=q.top().id;
		q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(Re int i=0;i<g[u].size();i++)
		{
			int to=g[u][i].id;
			int Len=g[u][i].d;
			if(d[to]>d[u]+Len)
			{
				d[to]=d[u]+Len;
				q.push((Node){to,d[to]});
			}
		}
	}
}

但是上述代码需要事先写一个结构体,有没有办法可以避免呢?

于是我们考虑使用 ( ext{pair}) 数组。

pair 数组

由于 ( ext{pair}) 数组有自带的比较函数,因此,我们可以省去上述的结构体。

但是我们需要注意一点,就是在入队的时候需要加入的是 (-d[v]) 而不是 (d[v])

void dijkstra()
{
	bool vis[N];
	memset(d,0x3f,sizeof d);
	memset(vis,0,sizeof vis);
	d[st]=0;
	priority_queue<pair<int,int> > q;
	q.push(make_pair(0,st));
	while(!q.empty())
	{
		int u=q.top().second;
		q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(Re int i=0;i<g[u].size();i++)
		{
			int to=g[u][i].id;
			int Len=g[u][i].d;
			if(d[to]>d[u]+Len)
			{
				d[to]=d[u]+Len;
				q.push(make_pair(-d[to],to));
			}
		}
	}
}
原文地址:https://www.cnblogs.com/kebingyi/p/14015661.html