1. 程式人生 > 實用技巧 >[luogu p1613] 跑路

[luogu p1613] 跑路

\(\mathtt{Link}\)

傳送門

\(\mathtt{Summarization}\)

給定一個有向圖 \(G\),進行一次操作可以走 \(2 ^ k\) 條邊,求 \(1 \rightarrow n\) 的最小運算元。

\(\mathtt{Solution}\)

看到一次操作 \(2 ^ k\) 自然想到倍增。

再看到如此弱的資料範圍,\(n \le 50\),一般五層迴圈巢狀都沒問題。

那就可以放心採用這樣一種做法了:

定義一個數組 dir,定義 dir[i][j][k] 為 從 i -> j 是否有一條長度為 \(2 ^ k\) 的路徑。

便可以想到一種暴力倍增的方案:每次列舉 \(k\)

,並列舉 \(u, v\) 兩個點,再列舉一個 \(i\) 作為中轉點。

有:

\[dir_{u,i,k-1} = \text{true}, dir_{i, v, k - 1} = \text{true} \rightarrow dir_{u, v, k} = \text{true} \]

至於初始條件,顯然:

\[u \rightarrow v \in G \rightarrow dir_{u, v, 0} = \text{true} \]

那麼,\(dir\)\(dis\) 之間的關係怎麼求呢?

對於兩個頂點 \(u, v\),若存在一個 \(k\) 使得 \(dir_{u, v, k} = \text{true}\)

,那麼 \(dis_{u, v} = 1\)

最後根據這些 \(dis\) 用 floyd 求最短路即可。

\(\mathtt{Time} \text{ } \mathtt{Complexity}\)

核心倍增複雜度:\(\mathcal{O}(\log dis \times n ^ 3)\)

floyd複雜度:\(\mathcal{O}(n^3)\)

所以整體複雜度是 \(\mathcal{O}(\log dis \times n ^ 3)\)

\(\mathtt{Code}\)

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2020-11-04 13:55:04 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2020-11-04 14:09:23
 */
#include <iostream>
#include <cstdio>
#include <cstring>

const int maxn = 55;
const int maxlogdis = 65;
inline int read() {
    char ch = getchar();
    int x = 0, f = 1;
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
inline int min(int a, int b) {
    return a < b ? a : b;
}

int dis[maxn][maxn];
bool dir[maxn][maxn][maxlogdis];

int main() {
    std :: memset(dir, 0, sizeof(dir));
    std :: memset(dis, maxn, sizeof(dis));
    
    int n = read(), m = read();
    for (int i = 1; i <= m; ++i) {
        int u = read(), v = read();
        dis[u][v] = 1;
        dir[u][v][0] = true;
    }

    for (int k = 1; k < maxlogdis; ++k)
        for (int u = 1; u <= n; ++u)
            for (int i = 1; i <= n; ++i)
                for (int v = 1; v <= n; ++v)
                    if (dir[u][i][k - 1] && dir[i][v][k - 1]) {
                        dir[u][v][k] = true;
                        dis[u][v] = 1;
                    }
    
    for (int i = 1; i <= n; ++i)
        for (int u = 1; u <= n; ++u)
            for (int v = 1; v <= n; ++v)
                dis[u][v] = min(dis[u][v], dis[u][i] + dis[i][v]);
    
    std :: printf("%d\n", dis[1][n]);
    return 0;
}

\(\mathtt{More}\)

看到這種弱資料範圍一定要放心,別想太複雜。