题目:
最初在一个记事本上只有一个字符 'A'。你每次可以对这个记事本进行两种操作:
Copy All (复制全部) : 你可以复制这个记事本中的所有字符(部分的复制是不允许的)。
Paste (粘贴) : 你可以粘贴你上一次复制的字符。
给定一个数字 n 。你需要使用最少的操作次数,在记事本中打印出恰好 n 个 'A'。输出能够打印出 n 个 'A' 的最少操作次数。示例 1:
输入: 3
输出: 3
解释:
最初, 我们只有一个字符 'A'。
第 1 步, 我们使用 Copy All 操作。
第 2 步, 我们使用 Paste 操作来获得 'AA'。
第 3 步, 我们使用 Paste 操作来获得 'AAA'。来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/2-keys-keyboard
动态规划:
通过示例一,我们发现如果打印3个字符,我们只能复制,并粘贴2次,所以进行了三个操作;
如果我们想要得到6个字符呢?最初只有一个,我们可以复制,并粘贴两次,这时有3个A,我们在复制,并粘贴的时候就可以得到6个A了;相比于一直复制一次我们步骤少了;
我们还可以复制,粘贴一次,这是有2个A,在复制粘贴两次,得到6个A;
所以我们发现如果想要得到合数(在大于1的整数中,除了1和这个数本身,还能被其他正整数整除的数)个字符时,可以通过复制因数子串,快速得到;
所以呢?我们可以使用动态规划的方式;
dp[i] 表示我们想要得到i个A需要最少进行dp[i]次操作
最大的步骤就是我们仅复制一次,一直粘贴知道获得i个A;即我们可以将dp[i]初始化为i
i%n==0 dp[i]=min(dp[i],dp[n]+i/n);
int minSteps(int n) {
if(n<=1)
return 0;
vector<int> dp(n+1,0);
for(int i=2;i<=n;++i)
{
dp[i]=i;
for(int j=2;j<i;++j)
{
if(i%j==0)
{
dp[i]=min(dp[i],dp[j]+i/j);
}
}
}
return dp[n];
}
上面的方法可以解决这个问题,但是效率实在是不怎么样
我们也可以算一下,时间复杂度O(n^2);空间复杂度O(n)
当然还有更高效的方法,就是官方解素数分解;
素数分解:
这种方法的思路是这个样子的,倘若N是一个合数,恰好P*Q=N;倘若我们将N个A分成两组 第一组为P个,第二组为Q个;第一组呢?可以是通过一次复制P-1次粘贴得到,第二组可是如果一个复制Q-1次粘贴;当P>=2&&Q>=2的时候 (P-1)*(Q-1)>=1一定成立,通过展开我们可以得到N=P*Q>=P+Q;
我们得到的这个式子的含义是什么呢? 倘若N=6;P=2,Q=3; 我们只需要得到2个A然后复制、粘贴2次即可(而复制、粘贴P子串的操作数为Q)
int minSteps(int n) {
if(n<=1)
return 0;
int d=2;
int result=0;
while(n>1)
{
//如果d是n的一个因数,我们需要得到一个n/d的子串进行d次操作
while(n%d==0)
{
result+=d;
n/=d;
}
d++;
}
return result;
}
注:如果本篇博客有任何错误和建议,欢迎伙伴们留言,你快说句话啊!