题目描述
Javier\texttt{Javier}Javier 为他的儿子 Luis\texttt{Luis}Luis 制作了一台特殊的计算器。显示屏可以显示 444 位数字(从 000 到 999999999999),机器上有三个按钮,分别对应三种操作:
- 按 +1+1+1 :显示屏上的数字加 111 ,然后对 100001000010000 取模(即只保留最后四位数字)。
- 按 ∗2*2∗2 :显示屏上的数字乘以 222 ,然后对 100001000010000 取模。
- 按 ÷3\div 3÷3 :显示屏上的数字除以 333 并向下取整(整数除法),不取模,因为结果自然小于 100001000010000。
Javier\texttt{Javier}Javier 设定一个起始数字,要求 Luis\texttt{Luis}Luis 通过最少的按键次数得到目标数字。请编写程序,对于每个测试用例输出最少按键次数。
输入格式
输入包含多个测试用例,每个测试用例占一行,每行有两个整数 aaa 和 bbb(0≤a,b≤99990 \le a, b \le 99990≤a,b≤9999),分别表示起始数字和目标数字。
文件结束符表示输入结束。
输出格式
对于每个测试用例,输出一行一个整数,表示从 aaa 到 bbb 的最少按键次数。
样例
输入
01024 5000 0 9999 6666
输出
11 1 2
(注意:样例输入中三个测试用例写在一行,实际输入可能每行一对数字,但输出要求每行一个整数,或者按原题描述是在一行内输出所有答案。本题已按单行输出通过验证。)
题目分析
本题本质上是一个 最短路径搜索 问题。每个数字 0∼99990 \sim 99990∼9999 可以看作图中的一个节点,从每个节点出发有三条有向边,分别对应三种操作:
- 加 111 取模: n→(n+1) mod 10000n \to (n + 1) \bmod 10000n→(n+1)mod10000
- 乘 222 取模: n→(n×2) mod 10000n \to (n \times 2) \bmod 10000n→(n×2)mod10000
- 整数除以 333 : n→⌊n/3⌋n \to \lfloor n / 3 \rfloorn→⌊n/3⌋
需要找到从起始节点 aaa 到目标节点 bbb 的最短路径长度(边数)。
由于节点总数只有 100001000010000 ,图的大小固定且较小,可以使用 广度优先搜索(BFS\texttt{BFS}BFS) 从 aaa 开始层层扩展,首次到达 bbb 时的层数即为最少按键次数。
为什么可以用 BFS\texttt{BFS}BFS ?
- 每条边的权值均为 111 (一次按键)。
- 图是无环的?不一定有环(比如加 111 取模会导致循环),但 BFS\texttt{BFS}BFS 可以正确处理有环图,只要标记已访问节点避免重复入队。
- 因为图规模小,BFS\texttt{BFS}BFS 保证在有限步内找到最优解。
状态空间与复杂度
- 状态总数: V=10000V = 10000V=10000
- 每个状态有 333 条出边,总边数 E=30000E = 30000E=30000
- BFS\texttt{BFS}BFS 时间复杂度为 O(V+E)=O(10000)O(V + E) = O(10000)O(V+E)=O(10000) ,即使对每个测试用例都重新 BFS\texttt{BFS}BFS ,也能轻松应对。
特殊情况
- 如果 a=ba = ba=b ,则答案为 000 ,不需要按键。
解题思路
-
输入处理
由于题目描述中样例输入将多个测试用例放在一行,但实际评测通常按每行一对整数处理,我们采用while (cin >> start >> target)的方式读取,直到文件结束。 -
BFS\texttt{BFS}BFS 实现细节
- 使用队列
queue<pair<int, int>>存储(当前数字, 步数),或分开存储步数数组。 - 使用
visited数组标记节点是否已访问,避免重复扩展。 - 从起始节点开始,依次生成三个后继节点:
nxt1 = (cur + 1) % 10000 nxt2 = (cur * 2) % 10000 nxt3 = cur / 3 - 若某后继节点等于目标数字,立即返回当前步数加 111 。
- 若未访问过,则入队并标记。
- 使用队列
-
输出格式
按照题目要求,每个测试用例的结果输出一行,因此每调用一次 BFS\texttt{BFS}BFS 就输出一行结果。 -
正确性保证
BFS\texttt{BFS}BFS 按层扩展,第一次遇到目标节点时的步数一定是最小的。因为所有边权相同,BFS\texttt{BFS}BFS 队列中的节点按步数单调递增。
代码实现
// The Calculator Machine
// UVa ID: 13069
// Verdict: Accepted
// Submission Date: 2026-06-11
// UVa Run Time: 0.120s
//
// 版权所有(C)2026,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
// 广度优先搜索求从 start 到 target 的最少按键次数
int bfs(int start, int target) {
if (start == target) return 0;
vector<int> visited(10000, 0);
queue<pair<int, int>> q;
q.push({start, 0});
visited[start] = 1;
while (!q.empty()) {
int cur = q.front().first;
int steps = q.front().second;
q.pop();
// 操作1: 加1
int nxt = (cur + 1) % 10000;
if (nxt == target) return steps + 1;
if (!visited[nxt]) { visited[nxt] = 1; q.push({nxt, steps + 1}); }
// 操作2: 乘2
nxt = (cur * 2) % 10000;
if (nxt == target) return steps + 1;
if (!visited[nxt]) { visited[nxt] = 1; q.push({nxt, steps + 1}); }
// 操作3: 除以3(整数除法)
nxt = cur / 3;
if (nxt == target) return steps + 1;
if (!visited[nxt]) { visited[nxt] = 1; q.push({nxt, steps + 1}); }
}
return -1; // 理论上不会执行到这里,因为图强连通
}
int main() {
int start, target;
// 读取所有测试用例,每行两个整数
while (cin >> start >> target) cout << bfs(start, target) << '\n';
return 0;
}
总结
本题是一道典型的状态空间 BFS\texttt{BFS}BFS 应用题,核心在于:
- 模型识别:将数字看作节点,操作看作边,问题转化为无权最短路径。
- 状态压缩:由于模数固定,状态空间有限(100001000010000),可以直接用数组存储访问标记和步数。
- BFS\texttt{BFS}BFS 的适用条件:边权相同,求最少步数,BFS\texttt{BFS}BFS 保证最优性。
技巧与关键点
- 注意 ÷3\div 3÷3 操作是整数除法,不需要再取模(结果自然小于 100001000010000 )。
- 加 111 和乘 222 后必须取模 100001000010000 ,否则会超出显示范围。
- 使用
visited数组避免重复访问,防止死循环。 - 由于状态数小,无需更高级的优化,直接 BFS\texttt{BFS}BFS 即可通过。
本题也可以使用双向 BFS\texttt{BFS}BFS 进一步优化,但在此规模下没有必要。通过本题,可以加深对 BFS\texttt{BFS}BFS 在图论最短路径问题中应用的理解。

1189

被折叠的 条评论
为什么被折叠?



