spfa最佳化

光風霽月發表於2024-03-08

\(spfa\) 的最佳化都是基於 \(deque\) 的,我們通常使用 \(LLL\) 最佳化,程式碼簡單,最佳化效果最好,詳情可見參考這裡,例題可以參考這裡

1. \(LLL\) 最佳化(入隊最佳化)

Large Label Last 最佳化:思路就是將 \(dist\) 更大的點放入隊尾,將 \(dist\) 更小的點放入隊頭,優先使用 \(dist\) 更小的點進行鬆弛操作。

void spfa(int head)
{
    memset(dist, 0x3f, sizeof dist);
    memset(st, false, sizeof st);
    dist[head] = 0;
    
    deque<int> q;
    q.push_front(head);
    st[head] = true;
    
    while(q.size())
    {
        int cur = q.front();
        q.pop_front();
        st[cur] = false;
        for(int i = h[cur]; i != -1; i = ne[i])
        {
            int j = e[i];
            if(dist[j] > dist[cur] + w[i])
            {
                dist[j] = dist[cur] + w[i];
                if(!st[j])
                {
                /*============== LLL 最佳化 ==============*/
                    if(q.empty() || dist[j] > dist[q.front()])
                        q.push_back(j);
                    else    q.push_front(j);
                /*======================================*/
                    st[j] = true;
                }
            }
        }
    }
}

2. \(SLF\) 最佳化(出隊最佳化)

Small Label First 最佳化:優先讓 \(dist\) 更小的節點出隊來進行鬆弛操作,不過這個“小”並不好把握,總不能遍歷一遍佇列找這個小的 \(dist\) 把。因此,這裡的“小”是平均意義上的小,即小於等於佇列中所有元素的平均值,因此我們要維護佇列元素個數 \(cnt\) (不用q.size(),呼叫函式可能更慢)和佇列中的元素和 \(sum\),透過 while 來進行判斷,不過由於最佳化邏輯中有個 while,其最佳化效果可能很玄?

void spfa(int head)
{
    memset(dist, 0x3f, sizeof dist);
    memset(st, false, sizeof st);
    dist[head] = 0;
    
    deque<int> q;
    q.push_front(head);
    st[head] = true;
    
    /*================================*/
    int sum = dist[head], cnt = 1;
    /*================================*/
    
    while(q.size())
    {
        int cur = q.front();
        /*============================*/
        while(dist[cur] * cnt > sum) 
        {
            q.push_front();
            q.push_back(cur);
            cur = q.front();
        }
        /*============================*/
        q.pop_front();
        st[cur] = false;
        /*============================*/
        cnt -- , sum -= dist[cur];
        /*============================*/
        for(int i = h[cur]; i != -1; i = ne[i])
        {
            int j = e[i];
            if(dist[j] > dist[cur] + w[i])
            {
                dist[j] = dist[cur] + w[i];
                if(!st[j])
                {
                    q.push_back(j);
                    /*============================*/
                    cnt ++ , sum += dist[j]; 
                    /*============================*/
                    st[j] = true;
                }
            }
        }
    }
}

相關文章