题目描述
最初记事本上只有一个字符 '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
提示:
解法
方法一:记忆化搜索
定义 $dfs(i)$ 为输出 $i$ 个字符的最少操作次数。初始化 dfs(1)=0
。
当 $i\gt 1$ 时,有:
$$
dfs(i)=\min _{j \mid i} (dfs(\frac{i}{j})+j, i), 2\leq j\lt i
$$
时间复杂度 $O(n\sqrt{n})$。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | class Solution:
def minSteps(self, n: int) -> int:
@cache
def dfs(n):
if n == 1:
return 0
i, ans = 2, n
while i * i <= n:
if n % i == 0:
ans = min(ans, dfs(n // i) + i)
i += 1
return ans
return dfs(n)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | class Solution {
private int[] f;
public int minSteps(int n) {
f = new int[n + 1];
Arrays.fill(f, -1);
return dfs(n);
}
private int dfs(int n) {
if (n == 1) {
return 0;
}
if (f[n] != -1) {
return f[n];
}
int ans = n;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) {
ans = Math.min(ans, dfs(n / i) + i);
}
}
f[n] = ans;
return ans;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | class Solution {
public:
vector<int> f;
int minSteps(int n) {
f.assign(n + 1, -1);
return dfs(n);
}
int dfs(int n) {
if (n == 1) return 0;
if (f[n] != -1) return f[n];
int ans = n;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) {
ans = min(ans, dfs(n / i) + i);
}
}
f[n] = ans;
return ans;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | func minSteps(n int) int {
f := make([]int, n+1)
for i := range f {
f[i] = -1
}
var dfs func(int) int
dfs = func(n int) int {
if n == 1 {
return 0
}
if f[n] != -1 {
return f[n]
}
ans := n
for i := 2; i*i <= n; i++ {
if n%i == 0 {
ans = min(ans, dfs(n/i)+i)
}
}
return ans
}
return dfs(n)
}
|
方法二:动态规划
记忆化搜索也可以改成动态规划。
$$
dp[i]=\min _{j \mid i} (dp[\frac{i}{j}]+j, i), 2\leq j\lt i
$$
时间复杂度 $O(n\sqrt{n})$。
| class Solution:
def minSteps(self, n: int) -> int:
dp = list(range(n + 1))
dp[1] = 0
for i in range(2, n + 1):
j = 2
while j * j <= i:
if i % j == 0:
dp[i] = min(dp[i], dp[i // j] + j)
j += 1
return dp[-1]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | class Solution {
public int minSteps(int n) {
int[] dp = new int[n + 1];
for (int i = 0; i < n + 1; ++i) {
dp[i] = i;
}
dp[1] = 0;
for (int i = 2; i < n + 1; ++i) {
for (int j = 2; j * j <= i; ++j) {
if (i % j == 0) {
dp[i] = Math.min(dp[i], dp[i / j] + j);
}
}
}
return dp[n];
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | class Solution {
public:
int minSteps(int n) {
vector<int> dp(n + 1);
iota(dp.begin(), dp.end(), 0);
dp[1] = 0;
for (int i = 2; i < n + 1; ++i) {
for (int j = 2; j * j <= i; ++j) {
if (i % j == 0) {
dp[i] = min(dp[i], dp[i / j] + j);
}
}
}
return dp[n];
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | func minSteps(n int) int {
dp := make([]int, n+1)
for i := range dp {
dp[i] = i
}
dp[1] = 0
for i := 2; i < n+1; i++ {
for j := 2; j*j <= i; j++ {
if i%j == 0 {
dp[i] = min(dp[i], dp[i/j]+j)
}
}
}
return dp[n]
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | function minSteps(n: number): number {
const dp = Array(n + 1).fill(1000);
dp[1] = 0;
for (let i = 2; i <= n; i++) {
for (let j = 1, half = i / 2; j <= half; j++) {
if (i % j === 0) {
dp[i] = Math.min(dp[i], dp[j] + i / j);
}
}
}
return dp[n];
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | /**
* @param {number} n
* @return {number}
*/
var minSteps = function (n) {
const dp = Array(n + 1).fill(1000);
dp[1] = 0;
for (let i = 2; i <= n; i++) {
for (let j = 1, half = i / 2; j <= half; j++) {
if (i % j === 0) {
dp[i] = Math.min(dp[i], dp[j] + i / j);
}
}
}
return dp[n];
};
|
方法三