单源最短路径2 | Dijkstra狄克斯特拉 | Single Source Shortest Path 2 | C/C++实现

问题描述

请编写一个程序,求给定加权有向图 G = ( V , E ) G=(V,E) G=(V,E)的单源最短路径的成本。请以G的顶点0为起点,输出0到各顶点v的最短路径上各边权值的总和d[v]。

输入: 第1行输入G的顶点数n。接下来n行按如下格式输入各顶点u的邻接表
u k v1 c1 v2 c2 … vk ck
G中的各顶点编号分别为0至n-1。u代表顶点的编号,k代表u的出度。vi(i = 1, 2, …, k)代表与u相邻顶点的编号,ci代表u到vi的有向边的权值。
输出: 按顺序输出各顶点编号v及距离d[v],相邻数据间用1个空格隔开。
限制:
1 ≤ n ≤ 10000
0 ≤ ci ≤ 100000
|E| < 500000
0到各顶点之间必然存在最短路径。

输入示例

5
0 3 2 3 3 1 1 2
1 2 0 2 3 4
2 3 0 3 3 1 4 1
3 4 2 1 0 1 1 4 4 3
4 2 2 1 3 3

输出示例

0 0
1 2
2 2
3 1
4 3

讲解

单源最短路径1 | Dijkstra狄克斯特拉 | Single Source Shortest Path 1 | C/C++实现中我们实现了一般形式的狄克斯特拉算法。这个算法由邻接矩阵实现,因此需要花费O(|V|)来给顶点u搜索相邻顶点v。此外,选择顶点u添加至最短路径树S的循环需要进行|V|次,所以算法复杂度为O(|V|^2)。即便改用邻接表,复杂度也不会有改观。

其实,只要我们采用邻接表表示法,在对二叉堆加以应用,就可以让狄克斯特拉算法的效率产生质的飞跃。

应用二叉堆实现的狄克斯特拉算法如下:

设图 G = ( V , E ) G=(V,E) G=(V,E)所有顶点的集合为V,起点为s,最短路径树种包含的顶点集合为S。在各计算步骤中,我们将选出最短路径树的边和顶点并将其添加至S。

对于各顶点 i ,设仅经由S内顶点的s到 i 的最短路径成本为d[i],i 在最短路径树中的父结点为p[i]。

1.初始状态下将S置空。初始化s的d[s] = 0;除s以外,所有属于V的顶点 i 的d[i] = 无限大。以d[i]为键值,将V的顶点构建成最小堆H。
2.循环进行下述处理,直至S = V为止
从H中取出d[u]最小的顶点u
将u添加至S,同时将与u相邻且属于V - S的所有顶点v的值按照下述方式更新

if d[u] + w(u, v) < d[v]
	d[v] = d[u] + w(u, v)
	p[v] = u
	以v为起点更新堆H

该算法可通过下述方式实现:

dijkstra(s)
	将所有顶点u的color[u]设为WHITE,d[u]初始化为INFTY
	d[s] = 0

	Heap heap = Heap(n, d)
	heap.construct()

	while heap.size >= 1
		u = heap.extractMin()

		color[u] = BLACK

		//如果仍存在与u相邻的顶点v
		while(v = next(u) ) != NIL
			if color[v] != BLACK
				if d[u] + M[u][v] < d[v]
					d[v] = d[u] + M[u][v]
					color[v] = GRAY
					heap.update(v)

我们可以像下面这样,用优先级队列代替二叉堆,将候选顶点插入队列:

dijkstra(s)
	将所有顶点u的color[u]设为WHITE,d[u]初始化为INFTY
	d[s] = 0

	PQ.push(Node(s, 0) )//将起点插入优先级队列
	//选s作为最开始的u

	while PQ 不为空
		u = PQ.extractMIN()

		color[u] = BLACK

		if d[u] < u的成本//取出最小值,如果不是最短路径则忽略
			continue

		//如果仍存在与u相邻的顶点v
		while(v = next(u) ) != NIL
			if color[v] != BLACK
				if d[u] + M[u][v] < d[v]
					d[v] = d[u] + M[u][v]
					color[v] = GARY
					PQ.push(Node(v, d[v]) )

AC代码如下

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
static const int MAX = 10000;
static const int INFTY = (1<<20);
static const int WHITE = 0;
static const int GRAY = 1;
static const int BLACK = 2;

int n;
vector<pair<int, int> > adj[MAX];//加权有向图的邻接表表示法

void dijkstra(){
	priority_queue<pair<int, int> > PQ;
	int color[MAX];
	int d[MAX];
	for(int i = 0; i < n; i++){
		d[i] = INFTY;
		color[i] = WHITE;
	}
	
	d[0] = 0;
	PQ.push(make_pair(0, 0));
	color[0] = GRAY;
	
	while( !PQ.empty() ){
		pair<int, int> f = PQ.top(); PQ.pop();
		int u = f.second;
		
		color[u] = BLACK;
		
		//取出最小值,如果不是最短路径则忽略
		if(d[u] < f.first * (-1) ) continue;
		
		for(int j = 0; j < adj[u].size(); j++){
			int v = adj[u][j].first;
			if(color[v] == BLACK) continue;
			if(d[v] > d[u] + adj[u][j].second){
				d[v] = d[u] + adj[u][j].second;
				//priority_queue默认优先较大值,因此要乘以-1
				PQ.push(make_pair(d[v] * (-1), v));
				color[v] = GRAY; 
			}
		} 
	}
	
	for(int i = 0; i < n; i++){
		cout<<i<<" "<<(d[i] == INFTY ? -1 : d[i])<<endl;
	}
} 

int main(){
	int k, u, v, c;
	
	cin>>n;
	for(int i = 0; i < n; i++){
		cin>>u>>k;
		for(int j = 0; j < k; j++){
			cin>>v>>c;
			adj[u].push_back(make_pair(v, c));
		}
	}
	
	dijkstra();
	
	return 0;
}
  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值