lgP1613跑路

题目用倍增进行确定路径, Floyd来找到答案。

用一个三元数组f[i][j[k], i 与 j 表示从点i到点j,k表示这条路的长度为2 ^ k,当f[i][j][k] = 1时表示存在这样一条路,反正则无。 这样预处理后,跑一次Floyd就可以得出答案。

有一点需要注意,题目中m <= 10000, 而2的14次方已经大于10000,但在倍增时外循环的值k却要大于14才能AC此题的原因是如图

我们可以发现把那个环跑两次就可以得到16的路程得到答案1,这时2 ^ 4大于边数10.所以由此可以推理当把环的长度变大时可能可以凑出一个k,这时2 ^ k 远大于10000.

#include<iostream>
#include<cstdio>
using namespace std;

const int inf = 777777;

int n, m;
int f[51][51][50], ans[51][51];

template<typename T>
inline void read(T &x){
    x = 0;
    T op = 1;
    char c = getchar();
    for(; c < '0' || c > '9'; c = getchar())
        if(c == '-')    op = -1;
    for(; c <= '9' && c >= '0'; c = getchar())
        x = (x << 3) + (x << 1) + c - '0';
    x *= op; 
} 

int main(){
    read(n), read(m);
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= n; ++j){
            if(i != j)  ans[i][j] = inf;
            else ans[i][j] = 0;
        }
    for(int i = 1; i <= m; ++i){
        int x, y;
        read(x), read(y);
        f[x][y][0] = 1;
        if(x != y) ans[x][y] = 1;   
    }
    for(int k = 1; k < 50; ++k)
        for(int v = 1; v <= n; ++v)
            for(int i = 1; i <= n; ++i)
                for(int j = 1; j <= n; ++j)
                    if(f[i][v][k - 1] && f[v][j][k - 1]){
                        f[i][j][k] = 1;
                        if(i != j) ans[i][j] = 1;
                    }
    for(int k = 1; k <= n; ++k)
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= n; ++j)
                ans[i][j] = min(ans[i][j], ans[i][k] + ans[k][j]);
    printf("%d\n", ans[1][n]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ZmeetL/p/11801188.html