P1613 跑路 TJ
思路
由题目 \(2^k\) 我们很容易联想到倍增,此时我们可以倍增\(+Floyd\),解决该问题。
首先我们设 \(g[i][j]\) 表示 \(i\) 和 \(j\) 之间的路程,显然,如果 \(g[i][j]\) 和 \(g[j][k]\) 同时为 \(1\) 我们就可以将 \(g[i][k]\) 设置成 \(1\),
由此我们新建一个图 \(t[i][j][k]\) 表示 \(i\) 和 \(j\) 之间是否有 \(2^k\) 的路,如果有,就可以相应的把 \(g[i][j]\) 设置成 \(1\),
此时我们可以初始化:如果 \(g[i][j] = 1\) 那么 \(t[i][j][0] = 1\),然后 \(DP\),
由于是倍增,容易得到状态转移方程 \(t[i][j][k] = (t[i][x][k - 1] = t[x][j][k - 1] = 1~ ? ~1 : 0)\),然后根据 \(t[i][j][k]\) 更新 \(g[i][j]\)。
最后用 \(g\) 进行一次 \(Floyd\) 即可
Folyd
还是利用 \(DP\) 的思想,每次加进去一个点,状态转移方程:\(g[i][j] = \min (g[i][j] ,g[i][k] + g[k][j])\)。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 51;
const int MAXM = 10010;
int n ,m;
int t[MAXN][MAXN][64];
int g[MAXN][MAXN];
void solve () {
for (int k = 1;k <= 64;++ k) {
for (int x = 1;x <= n;++ x) {
for (int q = 1;q <= n;++ q) {
for (int w = 1;w <= n;++ w) {
if (t[q][x][k - 1] == 1 && t[x][w][k - 1] == 1) {
t[q][w][k] = 1;
g[q][w] = 1;
}
}
}
}
}
}
void Floyed () {
for (int k = 1;k <= n;++ k) {
for (int q = 1;q <= n;++ q) {
for (int w = 1;w <= n;++ w) {
g[q][w] = min (g[q][w] ,g[q][k] + g[k][w]);
}
}
}
return ;
}
int main () {
memset (g ,0x3f ,sizeof (g));
memset (t ,0 ,sizeof (t));
scanf ("%d%d",&n ,&m);
int _from ,_to;
for (int q = 1;q <= m;++ q) {
scanf("%d%d",&_from ,&_to);
g[_from][_to] = 1;
t[_from][_to][0] = 1;//2 ^ 0 = 1
}
solve ();
Floyed ();
printf ("%d\n",g[1][n]);
return 0;
}
cb