题目描述
思路解析
一句话答案:LeetCode 650 只有两个键的键盘:只能复制全部和粘贴,凑够 n 个 A 求最少操作。把长度扩大 d 倍需 d 步,最优拆法是质因数分解,答案即 n 的质因子之和,试除法时间 O(√n)、空间 O(1)。
复制粘贴凑出 n 个 A,最少按几次
给一个正整数 n,屏幕初始只有 1 个 A。每次操作二选一:复制全部内容到剪贴板,或把剪贴板内容粘贴一次接到末尾。求最少多少次操作让屏幕恰好是 n 个 A。题面给三组:n=3 要 3 次,n=1 要 0 次,n=9 要 6 次。
为什么把每次复制粘贴都试一遍会爆
顺题意最容易想到把操作序列一步步试:每步要么复制、要么粘贴,铺开成一棵树往下搜哪条路最先凑够 n 个 A。可复制时机选择很多、序列长度没有上界,搜索树指数级膨胀,n 稍大就搜不完。浪费在于同一个中间长度被不同路径反复凑到,没记下来复用。
把长度扩大 d 倍,为什么正好花 d 步
别盯着一次次按键,看一整段操作。屏幕长度要变大,唯一办法是先复制一次、再连续粘贴若干次。当前有 k 个 A,复制 1 次后每粘贴一次多 k 个,粘贴 d-1 次总共变成 k 乘 d,即扩大到 d 倍。这一段花复制 1 次加粘贴 d-1 次,正好 d 次。
所以凑出 n 个 A,本质是把 n 写成若干个大于 1 的整数连乘,每个乘数 d 花掉 d 步,总步数就是这些乘数相加,求最少操作即找乘数之和最小的拆法。
dp 为什么沿因子转移,答案为什么是质因子和
把想法写成递推。设 dp[x] 是从 1 个 A 凑到 x 个 A 的最少操作。凑到 x 的最后一段是把某个因子 m 扩大到 x(m 能整除 x),倍数 x/m、花 x/m 步,接在 dp[m] 后。枚举 x 的所有因子取最小,dp[x]=min(dp[m]+x/m)。
把 n 拆成 a 乘 b 两段共花 a+b 步;只要 a、b 都不小于 2,就有 a+b 不超过 a 乘 b(比如 2+3=5 ≤ 2×3=6)。所以因子只要还是合数、还能再拆,拆开总步数只会不增。一路拆到剩下全是质数,各段之和最小,答案就是 n 的全部质因子相加。代码不必开 dp 数组,从最小质数试除 n,除得尽就累加进答案,时间 O(√n)。
拿 n=9 把因子拆法亲手走一遍
dp[1]=0,先看 dp[3]:因子只有 1,把 1 扩到 3 倍花 3 步,dp[3]=3。到 dp[9],因子有 1 和 3:走 1 是扩到 9 倍花 9 步得 9;走 3 是把 3 扩到 3 倍花 3 步、接在 dp[3]=3 后得 3+3=6,取小的 6,dp[9]=6,和题面对上。
换质因数分解看更快:9=3 乘 3,两个质因子相加 3+3=6。再看 n=3 本身是质数、只有 3 一个质因子,答案 3;n=1 没有质因子,答案 0——都和题面一致。
试除到 √n 就够,n=1 和质数两处别写错
试除只需从 2 到 √n:大于 √n 的因子至多剩一个,循环后若剩下的数还大于 1,把它当最后一个质因子补加,时间 O(√n)、空间 O(1)。
n=1 是头号陷阱——没有质因子,答案就是 0,别顺手返回 1。质数(7、13 这种)也容易漏,√n 以内没有因子整除它,循环一次都不加,全靠结尾那句『剩下的数大于 1 就补进来』兜住。还有个手滑:收因子时加的是因子 d 本身、不是次数 1,写成加 1 会把答案压小。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
记住这条:答案 = n 的所有质因子相加。下面用 n=60 一步步试除,把 60 拆成 2 x 2 x 3 x 5。
开局:要分解 60。从最小质数 d=2 开始试除,能整除就把 d 收进答案。
轮到 d=2(紫色)。先看 2×2=4 是否 ≤ 剩余 n=60,成立才继续试除;若 d×d 已大于剩余 n,说明没有更小因子可拆,循环退出,再把剩余 n(若 > 1)作为最后一个质因子补加。
60 能被 2 整除。说明这一段要把长度扩大 2 倍,花 2 步。把 2 收进答案。
收下质因子 2(绿色):ans 加 2 变成 2,剩余 n 缩成 30。继续看 30 还能不能再被 2 整除。
30 能被 2 整除。说明这一段要把长度扩大 2 倍,花 2 步。把 2 收进答案。
收下质因子 2(绿色):ans 加 2 变成 4,剩余 n 缩成 15。继续看 15 还能不能再被 2 整除。
15 不再被 2 整除,d=2 这一段拆完了。d 加 1,看下一个候选。
轮到 d=3(紫色)。先看 3×3=9 是否 ≤ 剩余 n=15,成立才继续试除;若 d×d 已大于剩余 n,说明没有更小因子可拆,循环退出,再把剩余 n(若 > 1)作为最后一个质因子补加。
15 能被 3 整除。说明这一段要把长度扩大 3 倍,花 3 步。把 3 收进答案。
收下质因子 3(绿色):ans 加 3 变成 7,剩余 n 缩成 5。继续看 5 还能不能再被 3 整除。
5 不再被 3 整除,d=3 这一段拆完了。d 加 1,看下一个候选。
试除循环结束后还剩 n=5,且 5 > 1,说明它是一个大质因子(再没有更小因子能拆它),直接收进答案。
加上最后这个质因子 5,ans = 12,剩余 n 归 1。60 = 2 × 2 × 3 × 5,质因子之和 = 12,就是最少操作次数。
边界:n=1 答 0;质数 n 答 n 本身(无法再拆)。
两个高频追问:一是证明拆质因子最优,二是 DP 写法与复杂度对比。
参考代码
class Solution: def minSteps(self, n: int) -> int: ans = 0 d = 2 while d * d <= n: while n % d == 0: ans += d n //= d d += 1 return ans + (n if n > 1 else 0)复杂度
- 时间:O(√n),d 只需试到 √n,最多 √n 次
- 空间:O(1),只用 ans、d、n 几个变量
易错点
面试追问把动画讲成自己的话
追问为什么质因数分解一定最优,而不是别的拆法?
追问也能用动态规划做吗?复杂度如何?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
最长递增子序列的个数
LeetCode 673 · 中等 · 沿着 动态规划套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题