650. 2 Keys Keyboard
问题
初始化有一个字符"A",可以进行两项操作:全部复制,或者粘贴。最少多少步能产生n个A。
Input: 3
Output: 3
Explanation: 全部复制(“A”),然后粘贴两次,三步操作。
思路
C记为全部复制,P记为粘贴。考虑多次连续复制是没有意义的,所以正常操作是一次C后面跟多次P。比如操作序列为CPPCPPPCPPPP,考虑每次C会对复制信息产生变化,可以根据C分组成[CPP] [CPPP] [CPPPP]。
[CPP] [CPPP] [CPPPP],第一个分组会产生长度为3的A,第二个分组是在第一个分组的基础上操作的,会产生3乘以4个A。容易发现以下结论,令各组的长度分别为g1, g2, g3,执行第一个分组后产生g1个A,执行第二个分组后产生g1*g2个A,执行第三个分组后产生g1*g2*g3个A。每个分组长度至少为2,因为只有复制C是产生不了A的。
所以我们实际要做的就是把n分解成多个数的乘积(每个数,即分组长度,至少为2),使得这些数加起来最小。
跟另一道题343. Integer Break相反,Integer Break是把n分解成多个数的和,使这些数的积最大。
那么如何分解呢,假设现在分成了两个数的乘积,n = n1 x n2,它们的和为n1 + n2,这个时候n1如果能继续分解成p和q使得和更小,则有n = p x q x n2,使得 (p + q + n2) < n1 + n2,此时 p + q < n1 = p x q。
也就是说只要因子可以分解成p和q,并且p + q < pq,我们就可以继续分解,使得和越来越小。什么时候p+q < pq,其实就是(p-1)(q-1)>1,二者等价。在正整数里,当p>=2且q>=3时即可。
如果这个数本身是质数,就不可分解了,否则的话,就可以一直分解,使得所有因子为质数。所以先从2开始分解,分到不能用2分解了,就用3,分到不能用3分解了,就用5,7,11,13等等(因为测试数据在1000以内,可以把1000以内的质数写出来)。
实际实现中,可以直接从2开始,逐一往上递增即可,即2,3,4,5,6,7,8,9。
时间复杂度最坏是O(N),比如997要从2一直遍历到997。最快是O(logN),比如1024,分10次2即可。
时间复杂度O(n),空间复杂度O(1)
代码
class Solution(object):
def minSteps(self, n):
"""
:type n: int
:rtype: int
"""
ans = 0
prime = 2
while n > 1:
while n % prime == 0:
ans += prime
n /= prime
prime += 1
return ans