两个键的键盘
650. 两个键的键盘
最初记事本上只有一个字符 'A'
。你每次可以对这个记事本进行两种操作:
Copy All
(复制全部):复制这个记事本中的所有字符(不允许仅复制部分字符)。Paste
(粘贴):粘贴 上一次 复制的字 符。
给你一个数字 n
,你需要使用最少的操作次数,在记事本上输出 恰好 n
个 'A'
。返回能够打印出 n
个 'A'
的最少操作次数。
示例 1:
输入:3
输出:3
解释:
最初, 只有一个字符 'A'。
第 1 步, 使用 Copy All 操作。
第 2 步, 使用 Paste 操作来获得 'AA'。
第 3 步, 使用 Paste 操作来获得 'AAA'。
示例 2:
输入:n = 1
输出:0
提示:
1 <= n <= 1000
数学+贪心
定义 x 次操作为 1 次 Copy All
+ x-1 次 Paste
,它会将原来的字符串变为原来的 x 倍。 注意 x 必须大于 1。
最小操作次数方案等价于以下操作流程:
- 起始对长度为 1 的记事本字符进行 1 次
Copy All
+ k1−1 次Paste
操作(消耗次数为 k1,得到长度为 k1 的记事本长度); - 对长度为为 k1 的记事本字符进行 1 次
Copy All
+ k2−1 次Paste
操作 (消耗次数为 k1+k2,得到长度为 k1∗k2 的记事本长度) - ...
最终经过 k 次“动作”之后,得到长度为 n 的记事本长度,即有:
n=k1∗k2∗...∗kx
问题转化为:如何对 n 进行因式分解,使得因子之和最小。
注意例子: 8=2*4,2+4=6<8,由此可知非平凡因式分解后因子之和会变小,所以我们必须尽可能地分解 n。
class Solution {
public int minSteps(int n) {
int ans = 0; // 初始化操作次数为 0
// 从 2 开始枚举可能的因子,直到 i * i > n,这是因为
// 1 是平凡因子,所以不用不考虑。当 i*i>n 时,i 不可能整除 n。
for (int i = 2; i * i <= n; i++) {
// 如果 i 是 n 的因子
while (n % i == 0) {
ans += i; // 将因子 i 累加到操作次数中
n /= i; // 用因子 i 将 n 除尽,因为我们要尽可能地分解 n
}
}
// 如果此时 n 不是 1,说明 n 本身是一个大素数。重点。
// 如果 n 不是素数,则上面的 for 循环一定能把 n 除到 1。
if (n != 1) ans += n; // 将剩余的素数因子累加到操作次数中
return ans; // 返回最小操作次数
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)