这道题的思路非常巧妙,如果两点之间的距离是$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; }