Dijkstra算法


题目链接:朴素版 优化版

题目描述

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。

输入描述

第一行包含整数 n 和 m。
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出描述

输出一个整数,表示 1 号点到 n 号点的最短距离。
如果路径不存在,则输出 −1。
朴素版:
1≤n≤500,
1≤m≤105,
图中涉及边长均不超过10000。
优化版:
1≤n,m≤1.5×105,
图中涉及边长均不小于 0,且不超过 10000。

示例

输入

3 3
1 2 2
2 3 1
1 3 4

输出

3

Dijkstra算法

Dijkstra算法是经典的解决无负权边单源最短路问题的算法,按照路径长度递增的次序产生最短路径。

该算法非常经典,具体算法过程不再细述,下面给出算法的核心及其证明。

我们设图G的顶点集合为V,再设一个集合S表示已求得最短路径的终点的集合(S怎么来的下面再说)。

设下一条最短路径(终点为x),那么它只能是弧(v,x)或者只通过S中的顶点到达x即(v,vi,….,x)。我们来证明一下:

假设(v,…,x)路径上有一个顶点不在S中,则说明存在一条终点不在S中而长度比此路径还短的路径。但这是不可能的。因为我们按长度递增的顺序来产生各最短路径,所以长度比此路径还短的所有路径均已产生,他们的终点一定在S中。

另外,如果要求到达某个点的最短路径的数量,可以在找到当前距离最近点后更新其它点距离时做记录,更新后如果等于原来的说明存在一条新的最短路径。

堆优化

算法中有一步是从未在S中的点当中选出到起点路径长度最短的一个点。对于这一步,我们可以通过一个小根堆来实现优化,大大降低这一步的时间复杂度。

朴素版代码


#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
const int N=501;
int n,m;
int dist[N];
int g[N][N];
bool st[N];

int dijkstra()
{
	memset(dist,0x3f,sizeof(dist));
	dist[1]=0;
	for(int i=0;i<n;i++)
	{
		int t=-1;
		for(int j=1;j<=n;j++)
		{
			if(!st[j] && (t==-1 || dist[t]>dist[j]))
				t=j;
		}
		st[t]=true;
		for(int j=1;j<=n;j++)
			dist[j]=min(dist[j],dist[t]+g[t][j]);
	}
	return dist[n]==0x3f3f3f3f? -1:dist[n];
}

int main()
{
	memset(g,0x3f,sizeof(g));
	cin >> n >> m;
	for(int i=0;i<m;i++)
	{
		int a,b,c;
		cin >> a >> b >> c;
		g[a][b]=min(g[a][b],c);
	}
	cout << dijkstra() << endl;
	return 0;
}

堆优化代码


#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <functional>

using namespace std;

typedef pair<int, int> P;
const int N = 1.5e5 + 10;
int n, m;
int dist[N];
int h[N], e[N], ne[N], w[N], idx;
bool st[N];	//标记已经入过堆的点 即最短距离已确定

void add(int a, int b, int c)
{
	e[idx] = b;
	w[idx] = c;
	ne[idx] = h[a];
	h[a] = idx++;
}

int dijkstra()
{
	memset(dist, 0x3f, sizeof(dist));
	dist[1] = 0;

	priority_queue<P, vector<P>, greater<P>> heap;
	heap.push(make_pair(0, 1));
	while (!heap.empty())
	{
		P t = heap.top();
		heap.pop();
		int d = t.first, v = t.second;
		if (st[v])		//一个点可能被多次更新从而多次入堆
			continue;	//第一次出堆为最优解
						//加以标记 可节省时间
		st[v] = true;
		for (int i = h[v]; i != -1; i = ne[i])
		{
			int j = e[i];
			if (dist[j] > w[i] + d)
			{
				dist[j] = w[i] + d;
				heap.push(make_pair(dist[j], j));
			}
		}
	}
	return dist[n] == 0x3f3f3f3f ? -1 : dist[n];
}

int main()
{
	memset(h, -1, sizeof(h));
	cin >> n >> m;
	for (int i = 0; i < m; i++)
	{
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c);
	}
	cout << dijkstra() << endl;
	return 0;
}

文章作者: Kong Aobo
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kong Aobo !
  目录