题目用倍增进行确定路径, 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;
}