题目描述
最初在一个记事本上只有一个字符 ‘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(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吗?
class Solution { public: int minSteps(int n) { vector<int> dp(n+1, INT_MAX); dp[1] =0; for (int i=1; i<=n; i++){ int cnt = 1; for (int j=i+i; j<=n; j = j+i){ cnt++; dp[j] = min(dp[j], dp[i] + cnt); } } return dp[n]; } };
这是我用刷表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)。
这就是从递归做法入手去理解为什么结果是质因数累加。
赞~