luogu P1613 跑路 题解 (倍增 && 最短路)

这道题的思路非常巧妙,如果两点之间的距离是$2^k$,那么就建一条长度为1的边,然后再跑一遍最短路就好了。

推的方法就是$f(i, j, len) = 1$ 如果 $f(i, k, len - 1) = 1$ 并且$f(k, j, len - 1) = 1$,其中$f(i, j, len)$指的是$i$到$j$是否有长度为$2^{len}$的边

有意思的是,如果一条路径是要重复走的,比如$i$和$j$之间有两条边,那么两个$2^0$的边存在,就可以推出$f(i, i)$存在$2^1$的边,可以看出这种情况是不会遗漏的。

代码如下

#include <cstdio>
using namespace std;
const int N = 51;
int dis[N][N][N], ans[N][N];
int main() {
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= m; i++) {
        int u, v;
        scanf("%d %d", &u, &v);
        dis[u][v][0] = ans[u][v] = 1;
    }
    for (int len = 1; len <= 32; len++)
        for (int k = 1; k <= n; k++)
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++)
                    if (dis[i][k][len - 1] == 1 && dis[k][j][len - 1] == 1)
                        dis[i][j][len] =  1, ans[i][j] = 1;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) 
            if (i != j && ans[i][j] != 1)
                ans[i][j] = (1 << 29);
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                if (ans[i][k] + ans[k][j] < ans[i][j])
                    ans[i][j] = ans[i][k] + ans[k][j];
    printf("%d", ans[1][n]);    
    return 0;        
}

 

posted @ 2020-03-14 16:53  cminus  阅读(127)  评论(0编辑  收藏  举报