针对 优先级队列优化过的dijkstra算法 以及 SPFA算法 进行讨论
一号选手:优先级队列优化过的dijkstra算法 (本质:bfs[ 队列改为优先级队列 ] + dis[]数组记录到达各点的距离 )
先贴出该算法的实现模板:
void dijkstra(int start) {
for(int i=1;i<=T;i++) {
dis[i]=inf;
}
dis[start]=0;
priority_queue<node> pq;
node nd;//临时存储变量使用
nd.at=start,nd.len=0;
pq.push(nd);
while(!pq.empty()) {
node fa=pq.top(); pq.pop();
for(int i=0;i<mp[fa.at].size();i++) {
node ch=mp[fa.at][i];
if(fa.len+ch.len<dis[ch.at]) {
dis[ch.at]=fa.len+ch.len;
nd.at=ch.at,nd.len=fa.len+ch.len;
pq.push(nd);
}
}
}
}
1、在没有负权边的情况下求某个点到其他所有点的距离
由于没有负权边,队列的top返回当前路径中最短的一条路径的可达点,那么到达该点的路径一定最短,
所以相同的点不会第二次进入队列,因为这行代码
fa.len+ch.len<dis[ch.at]
点评:耗时非常稳定,时间复杂度为elogv,在情况1下推荐优先使用。
2、在没有负权边的情况下求某个点到一个指定点的距离
由于没有负权边,所以可以在目标点进入队列的时候直接break跳出,即可以得到最短路径。
点评:在好的情况下可以快速找到结果,最坏情况下也不会耗时超过elogv,情况2下推荐优先使用。
3、有负权边的情况下求某个点到其他所有点的距离
由于存在负权边,队列的top不再是最短路径了,因为不知道哪里冒出一条负权边可能会打破原先的最短情况。
所以同一个点可能会入队列多次,并且一定要等到队列为空才可以跳出,否则dis【i】里的答案不一定正确的。
点评:耗时高昂,在情况3下,非常不推荐使用。
4、在有负权边的情况下求某个点到一个指定点的距离
由于存在负权边,所以与情况3一样,必须要等到队列为空才可以跳出。
点评:非常不推荐使用,即耗时高昂又显得弱智。
二号选手:SPFA算法 (本质:bfs[ 加一个数组判断点是否在队列内 ] + dis[]数组记录到达各点的距离 )
先贴出该算法的实现模板
void spfa(int start) {
for(int i=0;i<=T;i++) {
dis[i]=inf,has[i]=0;
}
dis[start]=0;
deque<int> dq;
dq.push_back(start);
while(!dq.empty()) {
int fa=dq.front(); dq.pop_front(); has[fa]=0;
for(int i=0;i<mp[fa].size();i++) {
node ch=mp[fa][i];
if(dis[fa]+ch.len<dis[ch.at]) {
dis[ch.at]=dis[fa]+ch.len;
if(!has[ch.at]) {
has[ch.at]=1;
dq.push_back(ch.at);
}
}
}
}
}
是否有负权边对SPFA算法不影响,直接讨论存在负权边的情况。
1、求某个点到其他所有点的最短路径
算法效率听说不错,但是非常不稳定,算法证明也有问题,自行百度SPFA算法。
点评:听说不错,实战也确实非常优秀,特别是在有负权边的情况下。
2、某个点到一个指定点的距离
这个与情况1一样,不管是否存在负权边,一定要等到队列为空才可以跳出,否则答案不一定正确。
总结:在没有负权边的情况下优先考虑dijkstra算法,比较稳定,当然SPFA也非常优秀。
有负权边的情况下那就使用SPFA算法,总体来说SPFA选手胜出。
最后说明一点, 没经过 优先级队列优化的dijkstra算法不能处理负权边的情况。
最最后,贴出经过 SLF优化 和 LLL优化的 SPFA算法,但是在实测中LLL优化反而增加了耗时。
测试例子是http://lx.lanqiao.cn/problem.page?gpid=T22
代码模板如下:
void spfa(int start) {
for(int i=0;i<=T;i++) {
dis[i]=inf,has[i]=0;
}
dis[start]=0;
deque<int> dq;
dq.push_back(start);
int sum=0,cnt=1; //LLL优化需要使用的两个变量
while(!dq.empty()) {
while(dis[dq.front()]*cnt>sum) {//LLL优化
dq.push_back(dq.front());
dq.pop_front();
}
int fa=dq.front(); dq.pop_front();
has[fa]=0;
sum-=dis[fa]; cnt--;
for(int i=0;i<mp[fa].size();i++) {
node ch=mp[fa][i];
if(dis[fa]+ch.len<dis[ch.at]) {
dis[ch.at]=dis[fa]+ch.len;
if(!has[ch.at]) {
has[ch.at]=1;
if(dis[ch.at]<dis[dq.front()]) {//SLF优化
dq.push_front(ch.at);
}else {
dq.push_back(ch.at);
}
sum+=dis[ch.at]; cnt++;
}
}
}
}
}