閱讀420 返回首頁    go 阿裏雲 go 技術社區[雲棲]


spfa優化 SLF LLL

2013.10.23 更新

之前寫的時候不知道在想什麼,整理模板時才發現寫挫了,現在重發一個


SPFA 是按照 FIFO 的原則更新距離的, 沒有考慮到距離標號的作用. 實現中 SPFA 有兩個非常著名的優化: SLF 和 LLL.

  SLF: Small Label First 策略.
  實現方法是, 設隊首元素為 i, 隊列中要加入節點 j, 在 d_j \le d_i 時加到隊首而不是隊尾, 否則和普通的 SPFA 一樣加到隊尾. 這可以用個優先級隊列維護

  LLL: Large Label Last 策略. 
  實現方法是, 設隊列 Q 中的隊首元素為 i, 距離標號的平均值為 \overline d = \frac {\sum_{j \in Q} d_j }{\left| Q \right|}, 每次出隊時, 若 d_i > \overline d, 把 i 移到隊列末尾, 如此反複, 直到找到一個 i 使 d_i \le \overline d, 將其出隊.

 

/**
    複雜度分析:
        普通SPFA km kmax=n 不適合稠密圖 一般為2
        優先級隊列  加入節點複雜度logn 節點數太多時適得其反,對於特殊數據速度略小於普通spfa
                     對於隨機圖效果很好
        手動模擬SLF,LLL 複雜度低於優先級隊列,最壞情況與普通SPFA持平

*/

#define Maxn 100010//最大點數
#define Maxm 400010//最大邊數,無向圖要建雙向邊
int w[Maxm],u[Maxm],next[Maxm],cnt;
int first[Maxn],havein[Maxn];//havin為入隊次數
long long d[Maxn];//距離
int n;
bool in[Maxn];//隊中標誌
inline void add(int vn,int un,int wn){//鄰接表存儲
    u[cnt]=un;w[cnt]=wn;next[cnt]=first[vn];first[vn]=cnt++;
}
struct node{
    int v,dd;
    node(int &a):v(a),dd(d[a]){};
    bool operator< (const node& a)const{
        return dd>a.dd;
    }
};
priority_queue<node> q; //利用優先級隊列SLF和LLL
bool spfa(int s){
    int i,now,ne,t;
    memset(in,0,sizeof(in));
    memset(havein,0,sizeof(havein));
    for(i=0;i<n;i++)d[i]=INF; //memset(d,0x3f,sizeof(d));
    d[s]=0;in[s]=1;q.push(s);
    while(!q.empty()){
        now=q.top().v;q.pop();
        if(!in[now])continue;
        in[now]=0;
        for(i=first[now];~i;i=next[i]){
            ne=u[i];
            if(d[ne]<=(t=d[now]+w[i]))continue;
            d[ne]=t; in[ne]=1; q.push(ne);
            if(++havein[ne]>n)return 0;//判斷有無負環
        }
    }
    return 1;//返回1為正常,0為有負環
}
#define M 200000  //手動模擬
int q[M];
bool spfa(int s){
    int i,now,ne,t;
    memset(in,0,sizeof(in));
    memset(havein,0,sizeof(havein));
    memset(d,0x3f,sizeof(d));
    int l,r,len;l=r=len=0;
    long long sum=0;
    d[s]=0;in[s]=havein[s]=1;
    q[r++]=s;len++;
    while(l!=r){
        now=q[l++];
        if(l==M)l=0;
        if(d[now]*len>sum){//LLL
            q[r++]=now;
            if(r==M)r=0;
            continue;
        }
        len--; sum-=d[now]; in[now]=0;
        for(i=first[now];~i;i=Next[i]){
            ne=u[i];
            if(d[ne]<=(t=d[now]+w[i]))continue;
            d[ne]=t;
            if(in[ne])continue;
            in[ne]=1;
            if(t<=d[q[l]]){ //SLF
                if(--l<0)l=M-1;
                q[l]=ne;
            }
            else{
                q[r++]=ne;
                if(r==M)r=0;
            }
            len++; sum+=t;
            if(++havein[ne]>n)return 0;
        }
    }
    return 1;//返回1為正常,0為有負環
}
void init()//邊初始化
{
    cnt=0;
    memset(first,-1,sizeof(first));
}


最後更新:2017-04-02 00:06:49

  上一篇:go 分析文件路徑、文件名、拓展名
  下一篇:go JSP基礎5