简述:

让我们考虑一下没有负边的情况。在Bellman-Ford算法中,如果dp[i]还不是最短距离的话,那么即使进行dp[j]=d[i]+(从I 到j的边的权值)的更新,dp[j]也不会变成最短距离。而且,即使dp[i]没有变化,每一次循环也要检查-遍从出发的所有边。这显然是很浪费时间的。因此可以对算法做如下修改。

(1)找到最短距离已经确定的顶点,从它出发更新相邻顶点的最短距离。

(2)此后不需要再关心1中的“最短距离已经确定的顶点”。

Dijkstra详解_最短距离

定义:

在(1)和(2)中提到的“最短距离已经确定的顶点”要怎么得到是问题的关键。在最开始时,只有起点的最短距离是确定的。而在尚未使用过的顶点中,距离dp[i]最 小的顶点就是最短距离已经确定的顶点。这是因为由于不存在负边,所以dp[i]不会在之后的更新中变小。这个算法叫做Dijkstra算法。

 

优化一:

使用邻接矩阵实现的Djkstra算法的复杂度是O(V^2)。使用邻接表的话,更新最短距离只需要访问

每条边一次即可,因此这部分的复杂度是O(E)。但是每次要枚举所有的顶点来查找下一个使用

的顶点,因此最终复杂度还是O(v^2)。在E比较小时,大部分的时间花在了查找下一个使用的顶

点上,因此需要使用合适的数据结构对其进行优化。首先想到了集合,使用vector来模拟集合即可。

实现:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n,m;//n个顶点m条边
const int INF=1e9;
vector<vector<int>>mp(1003,vector<int>(1003,INF));
vector<int>dp(1003,INF);
vector<int>unused;
void Dijkstra(int s) {
	dp[s]=0;
	unused.resize(n);
	for(int i=0; i<n; ++i)
		unused[i]=i;
	while(1) {
		int v=-1;
		for(int i=0; i<unused.size(); ++i)
			if((v==-1||dp[unused[i]]<dp[v]))//在没使用的顶点集中寻找离生成集合最近的点
				v=unused[i];
		if(v==-1)//若已没有没使用的点了,证明最短路径已全部更新完毕
			break;
		unused.erase(find(unused.begin(),unused.end(),v));//加入找到的点到生成集合中
		for(int i=0; i<unused.size(); ++i)
			dp[unused[i]]=min(dp[unused[i]],dp[v]+mp[v][unused[i]]);//更新因该点而改变未使用的点的最小dp值
	}
}
int main() {
	cin>>n>>m;
	for(int i=0; i<m; ++i) {
		int a,b,c;
		cin>>a>>b>>c;
		mp[a][b]=c;
		mp[b][a]=c;
	}
	int s;
	cin>>s;
	Dijkstra(s);
	for(int i=0; i<n; ++i)
		cout<<dp[i]<<" ";
}

 

优化二:

需要优化的是数值的插入(更新)和取出最小值两个操作,因此使用堆就可以了。把每个顶点当

前的最短距离用堆维护,在更新最短距离时,把对应的元素往根的方向移动以满足堆的性质。而

每次从堆中取出的最小值就是下一次要使用的顶点。这样堆中元素共有O(V)个,更新和取出数

值的操作有0(E)次,因此整个算法的复杂度是O(E *logV )。

下面是使用STL的priority_ queue 的实现。在每次更新时往堆里插人当前最短距离和顶点的值对。插入的次数是O(E)次,因此元素也是O(E)个。当取出的最小值不是最短距离的话,就丢弃这个值。这样整个算法也可以在同样的复杂度内完成。

来一个Pro版本: 

#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
int n,m;//n个顶点m条边
const int INF=1e9;
typedef pair<int,int> p;//first为dp值,second为顶点号 
vector<vector<int>>mp(1003,vector<int>(1003,INF));
vector<int>dp(1003,INF);
vector<int>used(1003,0);
void Dijkstra_Pro(int s) {
	dp[s]=0;
	priority_queue<p,vector<p>,greater<p>> q;
	q.push(p(0,s));
	while(!q.empty()) {
		p temp=q.top();
		q.pop();
		int v=temp.second;
		if(dp[v]<temp.first)//如果其最新dp值与过去的某一个更新时保存的dp值大小不一时放弃选择,因为过去的dp值已无效。
			continue;
		used[v]=1;
		for(int i=0; i<n; ++i)
			if(!used[i]) {
				int t=dp[i];
				dp[i]=min(dp[i],dp[v]+mp[v][i]);//更新因该点而改变未使用的点的最小dp值 
				if(t!=dp[i])//当且仅当dp[i]已更新时才加入优先队列中 
					q.push(p(dp[i],i));
			}
	}
}
int main() {
	cin>>n>>m;
	for(int i=0; i<m; ++i) {
		int a,b,c;
		cin>>a>>b>>c;
		mp[a][b]=c;
		mp[b][a]=c;
	}
	int s;
	cin>>s;
	Dijkstra_Pro(s);
	for(int i=0; i<n; ++i)
		cout<<dp[i]<<" ";
}

 

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6