【刷题日记】P1613 跑路(倍增优化动态规划)
题目
题目描述
小 A 的工作不仅繁琐,更有苛刻的规定,要求小 A 每天早上在 \(6:00\) 之前到达公司,否则这个月工资清零。可是小 A 偏偏又有赖床的坏毛病。于是为了保住自己的工资,小 A 买了一个空间跑路器,每秒钟可以跑 \(2^k\) 千米(\(k\) 是任意自然数)。当然,这个机器是用 longint 存的,所以总跑路长度不能超过 maxlongint 千米。小 A 的家到公司的路可以看做一个有向图,小 A 家为点 \(1\),公司为点 \(n\),每条边长度均为一千米。小 A 想每天能醒地尽量晚,所以让你帮他算算,他最少需要几秒才能到公司。数据保证 \(1\) 到 \(n\) 至少有一条路径。
输入格式
第一行两个整数 \(n,m\),表示点的个数和边的个数。
接下来 \(m\) 行每行两个数字 \(u,v\),表示一条 \(u\) 到 \(v\) 的边。
输出格式
一行一个数字,表示到公司的最少秒数。
输入输出样例 #1
输入 #1
4 4
1 1
1 2
2 3
3 4
输出 #1
1
说明/提示
【样例解释】
\(1 \to 1 \to 2 \to 3 \to 4\),总路径长度为 \(4\) 千米,直接使用一次跑路器即可。
【数据范围】
\(50\%\) 的数据满足最优解路径长度 \(\leq 1000\);
\(100\%\) 的数据满足 \(2\leq n \leq 50\),\(m \leq 10 ^ 4\),最优解路径长度 \(\leq\) maxlongint。
题解
题目给的 $ n $ 非常小!
如果从 $ i $ 到 $ t $ 的距离是 $ 2^{k-1} $ ,从 $ t $ 到 $ j $ 的距离也是 $ 2^{k-1} $ ,那从 $ i $ 到 $ j $ 的距离就是 $ 2^{k} $ 。
那么有个小问题:如果从 $ i $ 到 $ t $ 的距离不是 $ 2^{k-1} $ ,但从 $ i $ 到 $ t $ 再到 $ j $ 的距离是 $ 2^{k} $ 怎么办呢?那么从 $ i $ 去 $ t $ 或者从 $ t $ 去 $ j $ 的路上一定有一个新的 $ t' $ 满足上一自然段描写的 $ t $ 。比如样例中 $ 1 $ 到 $ 3 $ 再 $ 3 $ 到 $ 4 $ 的距离是 $ 2^2 $ ,那么选从 $ 1 $ 到 $ 3 $ 路上的新的 $ t' $ 为 $ 2 $ ,一定可以找到 $ 1 $ 到 $ 4 $ 距离为 $ 2^2 $ 。
那么我们的想法就是不会漏掉任何情况的,直接倍增法去动态规划,找出所有点与点之间距离为 $ 1 $ 的路径,然后跑一遍 $ Floyd $ 就可以了。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
i64 dis[60][60], g[60][60][65], n, m, u, v;
int main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
memset(dis, 10, sizeof(dis));
for(i64 i = 1;i<=m;i++){
cin >> u >> v;
dis[u][v] = 1;
g[u][v][0] = 1;
}
for(i64 k = 1;k<=64;k++) //倍增,枚举k
for(i64 i = 1;i<=n;i++)
for(i64 t = 1;t<=n;t++)
for(i64 j = 1;j<=n;j++)
if(g[i][t][k-1] && g[t][j][k-1]) g[i][j][k] = 1, dis[i][j] = 1;
for(i64 k = 1;k<=n;k++) //Floyd要先枚举中间点
for(i64 i = 1;i<=n;i++)
for(i64 j = 1;j<=n;j++)
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
cout << dis[1][n];
return 0;
}

浙公网安备 33010602011771号