题目描述
最初在一个记事本上只有一个字符 ‘A’。你每次可以对这个记事本进行两种操作:
Copy All (复制全部)
: 你可以复制这个记事本中的所有字符(部分的复制是不允许的)。
Paste (粘贴)
: 你可以粘贴你上一次复制的字符。
给定一个数字 n
。你需要使用最少的操作次数,在记事本中打印出恰好 n
个 ‘A’。输出能够打印出 n
个 ‘A’ 的最少操作次数。
样例
输入:3
输出:3
解释:
最初, 我们只有一个字符 'A'。
第 1 步, 我们使用 Copy All 操作。
第 2 步, 我们使用 Paste 操作来获得 'AA'。
第 3 步, 我们使用 Paste 操作来获得 'AAA'。
注意
n
的取值范围是[1, 1000]
。
算法1
(动态规划) $O(n)$
- 设计状态 $f(i)$,表示构成 $i$ 个
A
所需要的最少步数,注意这里只需要计算是 $n$ 的约数的状态,即状态 $i$,满足 $n \% i == 0$。 - 初始时,$f(1) = 0$,其余为正无穷;每次转移时,枚举 $i$ 的约数 $j$,即 $i \% j = 0$ 则有 $f(i) = \min (f(i), f(j) + \frac{i}{j})$。
- 最终答案为 $f(n)$。
时间复杂度
- 状态数有 $O(\sqrt n)$个,每个状态的转移有 $O(\sqrt n)$个,故总时间复杂度为 $O(n)$。
C++ 代码
class Solution {
public:
int minSteps(int n) {
vector<int> f(n + 1, INT_MAX);
f[1] = 0;
for (int i = 2; i <= n; i++)
if (n % i == 0)
for (int j = 1; j * j <= n; j++)
if (i % j == 0) {
f[i] = min(f[i], f[j] + i / j);
f[i] = min(f[i], f[i / j] + j);
}
return f[n];
}
};
算法2
(数学) $O(\sqrt n)$
- 假设已经得到了 $n$ 的所有质因数 $a_1, a_2, …, a_i$,则 $n$ 的分解方式可能如下:最后一步必定是由 $a_1$ 次 $\frac{n}{a_1}$ 个
A
操作而成;同理,倒数第二步一定是由 $a_2$ 次 $\frac{n}{a_1 a_2}$ 个A
操作而成,直到第一步为止。可以发现不管分解的顺序如何,最终答案都是质因数之和。核心思想即粘贴的次数为质数次,否则可以有更有的分解方式。 - 简要证明如下,设当前字符串的长度为 $l$,若直接拷贝达到目标长度 $lp$ 需要 $p$ 次操作。若 $p$ 为合数,即 $p = ij$ 且 $i>1, j>1$,则可以将拷贝分解为两次,先操作 $i$ 次,然后再操作 $j$ 次,这样需要 $i+j$ 步完成,显然 $ij >= i+j$,所以,每次操作的次数必定是质数次。这样我们只需要所有质因数累加即可。
时间复杂度
- 质因数的个数为 $O(\sqrt n)$,故时间复杂度为 $O(\sqrt n)$。
C++ 代码
class Solution {
public:
int minSteps(int n) {
int ans = 0;
int i = 2;
while (n > 1) {
while (n % i == 0) {
n /= i;
ans += i;
}
i++;
}
return ans;
}
};
方法1可以稍微做一点优化吧,里面if (i > 1)好像不需要吧, i是从2开始遍历的
done
能解释下算法1为什么状态数是n^0.5吗?
这是我用刷表ac的代码,时间是O(n^1.5)
因为只有 n % i == 0 的状态有意义
int minSteps(int n) {
if (n == 1) return 0;
for (int i = 2; i < n; i++)
if (n % i == 0) return i + minSteps(n / i);
return n;
}
那个递归做法,效率不如直接求质因数高,但是我觉得好理解一些,for循环就是个判断素数(质数)的,如果n是合数的话就递归的计算,如果是质数的话可以直接返回(质数就是由n次操作得来的)。
这道题可不可以这样子理解:如果n是质数的话,得到这个数的过程就是一次的复制‘A’的次数加上(n-1)次黏贴的过程,操作的总次数是n次;如果一个数是合数的话,举个例子:n=18,变成18所需要的次数=2+变成9所需要的次数(对应于minSteps(18/2))。变成9所需要的次数=3+变成3所需要的次数(对应于minSteps(9/3))。3是质数,直接返回3。每一步都是在递归的求最小质因数。所以答案就是2+3+3=8次操作。
这样子一想,结果也就是n的质因数累加。思想就像动态规划做法中的 dp[i] = min(dp[i], dp[j]+i/j)。
这就是从递归做法入手去理解为什么结果是质因数累加。
赞~