圖論最短路演算法筆記

Redlight_S發表於2024-11-24

1 圖的基本操作

1.1 圖的儲存

圖儲存有兩種方法:鄰接表鄰接矩陣

  • 鄰接表
g[N][N] = {};
...
memset(g, 0x3f, sizeof g);
g[u][v] = w;
  • 鄰接矩陣
int head[N] = {};
memset(head, 0x3f, sizeof head); 
struct edge{
    int pre, to, val;
}EDGE[N];
inline void addedge(int u, int v, int w, int i){
    EDGE[i] = {v, w, head[u]};
    head[u] = i;
}

1.2 圖的遍歷

圖的遍歷是指從圖中的任一頂點出發,對圖中的所有頂點訪問一次且只訪問一次(訪問一次,但不止用到一次)。 圖的遍歷操作和樹的遍歷操作功能相似。

2 最短路演算法

圖求最短路有演算法:

  • Floyd
  • Dijkstra
  • SPFA(死了)
  • Bellman-Ford
  • ...

2.1 Floyd

  • 用途:求任意兩個結點之間的最短路
  • 複雜度:\(O(n^3)\)
  • 適用:適用於任何圖,不管有向無向,邊權正負,但是最短路必須存在
for(reg int k=1; k<=n; ++k)
    for(reg int i=1; i<=n; ++i)
        for(reg int j=1; j<=n; ++j)
            if(g[i][k] + g[k][j] < g[i][j])
                g[i][j] = g[i][k]+g[k][j];

以上:g 為鄰接矩陣。

2.2 Dijkstra

  • 用途:單源最短路徑
  • 複雜度:\(O(n^2)\) -> \(O(nlogn)\)
  • 適用:非負權圖

步驟

  • 1 初始化:
    源點 \(u\)dis[N]book[N]
    建立集合 \(S\)\(V-S\)。剛開始,只有 \(u\)\(S\) 中。
vector<edge> e[MAXN];
int dis[MAXN], book[MAXN];

image

  • 2 找 dis[] 最小:
    開始,dis[n] 為 源點 \(u \rightarrow n\) 的特殊最短路。
    尋找 dis[n] 中最小的節點 \(t\)(可最佳化)。
    image
    image
  • 3 加入集合 \(S\)
    加入集合 \(S\),現在 \(S\) 表示為最短路的部分。
    image
  • 4 借東風
    與 Floyd 較為類似,就是以一個節點 \(i\) 作中轉點,看看能不能將與周圍的節點 \(k_1, k_2, ..., k_n\) 的邊 \(k_1 \rightarrow i \rightarrow k_2\) 的長度減小(鬆弛)。
    image
  • 5 判結束
    如果 \(V-S\) 集合為空集,那麼全部的 dis[] 都處理完畢,這時的 dis[n] 為 源點 \(u \rightarrow n\) 的最短路。
    image

樸素演算法(沒寫過,來自 oi-wiki):

struct edge {
    int v, w;
};
vector<edge> e[MAXN];
int dis[MAXN], vis[MAXN];
void dijkstra(int n, int s) {
    memset(dis, 0x3f, (n + 1) * sizeof(int));
    dis[s] = 0;
    for (int i = 1; i <= n; i++) {
        int u = 0, mind = 0x3f3f3f3f;
        for (int j = 1; j <= n; j++)
            if (!vis[j] && dis[j] < mind)
                u = j, mind = dis[j];
        vis[u] = true;
        for (auto ed : e[u]) {
            int v = ed.v, w = ed.w;
            if (dis[v] > dis[u] + w) 
                dis[v] = dis[u] + w;
        }
    }
}

堆最佳化

堆最佳化就是用堆進行最佳化,而樸素的演算法是“掃描”一遍圖找最小的點。而小根堆優先佇列最佳化就可以快速維護最小的點,大大減少時間複雜度( \(O(nlogn)\) )。

struct edge {
    int pre, to, val;
}EDGE[MAXN];
int dis[MAXN], book[MAXN], head[MAXN];

inline void addedge(int u, int v, int w, int i){
    EDGE[i] = {v, w, head[u]};
    head[u] = i;
}

inline void dijkstra(int s){
    memset(dis, inf, sizeof dis);
    priority_queue <pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > heap;
    heap.push({0, s});
    while(!heap.empty()){
        int t = heap.top().second;
        heap.pop();
        if(book[t])
            continue;
        book[t] = true;
        for(reg int i=head[t]; i; i=EDGE[i].pre){
            if(dis[EDGE[i].to] > EDGE[i].val+dis[t]){
                dis[EDGE[i].to] = EDGE[i].to+dis[t];
                heap.push({dis[EDGE[i].to], EDGE[i].to});
            }
        }
    }
    return;
}

相關文章