7.11 Update
我做题的时候发现这样写会RE
因为在使用双端队列优化SPFA的时候 在将一个点加入队列的时候,如果队列已经空了 那么一旦出现dis[Q.front()]就会RE 可以这样修改
if(!Q.empty()) { if(dis[v[k]] < dis[Q.front()]) Q.push_front(v[k]); else Q.push_back(v[k]); } else Q.push_front(v[k]);
这样就不会RE了
期望时间复杂度:O(k*e或me)//k是增长很快的函数ackermann的反函数,2^65536次方也就5以下,但是可以被恶意数据卡掉,起复杂度就位(n*n ) //其中m为所有顶点进队的平均次数,可以证明m一般小于等于2n:“算法编程后实际运算情况表明m一般没有超过2n.事实上顶点入队次数m是一个不容易事先分析出来的数,但它确是一个随图的不同而略有不同的常数.所谓常数,就是与e无关,与n也无关,仅与边的权值分布有关.一旦图确定,权值确定,原点确定,m就是一个确定的常数.所以SPFA算法复杂度为O(e).证毕."(SPFA的论文)不过,这个证明是非常不严谨甚至错误的,事实上在bellman算法的论文中已有这方面的内容,所以国际上一般不承认SPFA算法。
SPFA算法有两个优化策略SLF和LLL——SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾; LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出队进行松弛操作。 SLF 可使速度提高 15 ~ 20%;SLF + LLL 可提高约 50%。 在实际的应用中SPFA的算法时间效率不是很稳定,为了避免最坏情况的出现,通常使用效率更加稳定的Dijkstra算法。
上面这两段话呢,来自百度。
关于SPFA的时间复杂度,地球人应该都知道非常玄学的,近似可以看作O(看脸),
对于上面所说的最坏情况,我还记得有一次考试的题目中有一道最短路问题。
那道题的最后一组数据是用来卡SPFA的,(吓,出题人好毒瘤
这里我们不介绍LLL优化,
Only SLF优化
在每一次松弛操作的时候都已进入队列的操作
可是朴素的SPFA中将元素放到队列中时无序的,
如果改用一种很吊的队列的话,将其中的元素变得有点儿顺序
就可以起到优化的作用
这便是SLF优化
我们使用c++STL中的deque来实现上述操作
建议Pascal选手尽快转C++吧
下面就是代码(可能会很丑哦)
代码
#include <iostream> #include <cstdio> #include <cstring> #include <deque> #include <algorithm> const int maxnode = 1e4+3; const int maxedge = 5e5+3; #define INF 2147483647 using namespace std; deque<int> Q; int first[maxnode], next[maxedge], n, m, s; int u[maxedge], v[maxedge], w[maxedge], dis[maxnode]; bool vis[maxnode]; inline int read() { char c = getchar(); int x = 0, f = 1; while (c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while (c <= '9' && c >= '0') { x = x*10 + c-'0'; c = getchar(); } return x * f; } inline void addedge(int from, int i) { next[i] = first[from]; first[from] = i; } inline void SPFA(int sta) { Q.push_back(sta), vis[sta] = true; while (!Q.empty()) { int x = Q.front(); int k = first[x]; Q.pop_front(); while (k != -1) { if (dis[v[k]] >= dis[u[k]] + w[k]) { dis[v[k]] = dis[u[k]] + w[k]; if (!vis[v[k]]) { vis[v[k]] = 1; if (dis[v[k]] < dis[Q.front()]) Q.push_front(v[k]); else Q.push_back(v[k]); } } k = next[k]; } vis[x] = 0; } } int main() { n = read(), m = read(), s = read(); for (int i=1; i<=n; i++) dis[i] = INF; dis[s] = 0; memset(first, -1, sizeof(first)); for (int i=1; i<=m; i++) { u[i] = read(), v[i] = read(), w[i] = read(); addedge (u[i], i); } SPFA(s); for (int i=1; i<=n; i++) printf("%d ", dis[i]); }