题解:学而思编程 增长或翻倍
【题目来源】
学而思编程:增长或翻倍
【题目描述】
给定正整数 \(s\) 和 \(t\),保证 \(s \le t\),我们希望利用增长或翻倍的操作,让 \(s\) 变成 \(t\)。
增长操作可以让数字加一,即 \(x\rightarrow x+1\);
翻倍操作可以让数字翻倍,即 \(x\rightarrow 2\times x\)。
请问最少需要用多少步操作才能将 \(s\) 变成 \(t\) ?
【输入】
一行两个正整数,分别表示 \(s\) 与 \(t\)。
【输出】
一行一个整数,表示最少操作步数。
【输入样例】
1 4
【输出样例】
2
【核心思想】
-
问题分析:给定起始值 \(s\) 和目标值 \(t\)(\(s \le t\)),通过增长(\(x \rightarrow x+1\))和翻倍(\(x \rightarrow 2x\))两种操作,求将 \(s\) 变为 \(t\) 的最少操作步数。这是一个逆向贪心问题,关键在于从 \(t\) 反向推导到 \(s\) 时,每一步选择最优的反向操作。
-
算法选择:
- 逆向思维:不从 \(s\) 正向枚举所有操作路径,而是从 \(t\) 反向操作回 \(s\),大幅降低搜索空间
- 贪心策略:在反向过程中,优先选择除以 2(对应正向的翻倍),当且仅当 \(t\) 为偶数且 \(t/2 \ge s\) 时执行;否则执行减 1(对应正向的增长)。这种策略保证每一步都尽可能"大步前进"
-
关键步骤:
- 初始化:读取 \(s\)(起始值)、\(t\)(目标值),初始化步数计数器 \(step = 0\)
- 逆向循环(当 \(t \neq s\) 时继续):
- 判断能否除以 2:若 \(t \bmod 2 = 0\) 且 \(t/2 \ge s\),则执行 \(t \leftarrow t/2\),\(step \leftarrow step + 1\)
- 选择理由:翻倍是增长效率最高的操作,反向时除以 2 能最大幅度缩小 \(t\) 与 \(s\) 的差距
- 约束条件 \(t/2 \ge s\):确保除以 2 后不会"越过" \(s\),避免后续需要额外操作补回
- 否则执行减 1:\(t \leftarrow t - 1\),\(step \leftarrow step + 1\)
- 选择理由:当 \(t\) 为奇数或 \(t/2 < s\) 时,除以 2 会导致结果小于 \(s\),不如逐步减 1 精确逼近
- 判断能否除以 2:若 \(t \bmod 2 = 0\) 且 \(t/2 \ge s\),则执行 \(t \leftarrow t/2\),\(step \leftarrow step + 1\)
- 终止条件:当 \(t = s\) 时,输出 \(step\)
-
时间/空间复杂度:
- 时间复杂度:\(O(\log t)\),每次除以 2 操作使 \(t\) 指数级减小,减 1 操作最坏情况下连续执行但受限于奇数间隔
- 空间复杂度:\(O(1)\),仅使用常数个变量
-
逆向贪心的核心思想:
- 操作可逆性:增长(\(+1\))的逆操作是减 1,翻倍(\(\times 2\))的逆操作是除以 2,逆向过程与正向过程一一对应
- 贪心最优性:从 \(t\) 反向时,优先选择"大步"(除以 2)总是优于或等于"小步"(减 1)的累积效果,因为 \(\times 2\) 是指数增长,其逆操作是指数缩减
- 边界保护:条件 \(t/2 \ge s\) 防止贪心过度,确保每一步反向操作都在有效范围内
- 与正向 BFS/DP 的对比:正向从 \(s\) 出发需要枚举两种分支,时间复杂度随步数指数爆炸;逆向从 \(t\) 出发每一步决策唯一, greedy 即可得到全局最优
- 适用于操作可逆、且存在"高效操作"与"低效操作"之分的最短步数问题
【算法标签】
数学
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int s, t, step; // s: 起点, t: 终点, step: 步数
int main()
{
cin >> s >> t; // 输入起点和终点
while (s != t) // 当起点不等于终点时循环
{
if (t % 2 == 0 && t / 2 >= s) // 如果t是偶数且t/2≥s
{
t /= 2; // 反向操作:除以2
}
else
{
t--; // 反向操作:减1
}
step++; // 步数加1
}
cout << step; // 输出最小步数
return 0;
}
【运行结果】
1 4
2
浙公网安备 33010602011771号