【刷题日记】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;
}
posted @ 2026-02-02 22:30  Alkaid16  阅读(1)  评论(0)    收藏  举报