阅读 574

力扣:650 只有两个键的键盘

小知识,大挑战!本文正在参与「程序员必备小知识」创作活动

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

题源:只有两个键的键盘

题目描述

最初记事本上只有一个字符 'A' 。你每次可以对这个记事本进行两种操作:

Copy All(复制全部):复制这个记事本中的所有字符(不允许仅复制部分字符)。 Paste(粘贴):粘贴 上一次 复制的字符。 给你一个数字 n ,你需要使用最少的操作次数,在记事本上输出 恰好 n 个 'A' 。返回能够打印出 n 个 'A' 的最少操作次数。

初读这个题,感觉就应该是动态规划,但是找不出状态转移方程,当看到题解之后还是感到非常遗憾,就差一点点。

1. 思路与想法

题目中只有两种操作,copypaste,其中copy必须拷贝当前的所有内容,最终得出nA所需操作的最小次数。

最小次数,copy全部,这两个在一开始就给予了我贪心的感觉,对于nA来说,它期望的上一次操作是n/2处整体复制粘贴过来,n/2n/4...依次类推,就可以得到最小的次数。但很快我发现了上述思想的漏洞,举个例子,如果此时的n15,那就没法通过n/2->n/4这种模式。

15 = 3 * 515虽然不能通过上述的模式得来,但可以通过因数3来操作得来。但很可惜在这里我并没有思考到质因数的层面。

分析到上面,那么问题的实现就简单了:

  • 对于质数,它无法通过任何因数加速它,所以他的操作数就是它本身
  • 对于合数,它可以通过因数来加速操作,但当时苦于想不出动态规划的转移方程,我选择从头到尾构建一个存储[1,n]的数组,数组值为最小操作数。

1.1 算法流程

  • freq[1-n]依次赋值为1-n,修改freq[1] = 0
  • 遍历2-n,对于每次遍历更新其小于等于n的所有倍数的最小次数
  • 全部遍历完成后,返回freq[n]

1.2 代码

def minSteps(n):
    freq = [x for x in range(n + 1)]
    freq[1] = 0
    for i in range(2, n + 1):
        j = 2
        tmp = i * j
        while tmp <= n:
            # 依次更新当前i的倍数
            freq[tmp] = min(freq[i] + j, freq[tmp])
            j += 1
            tmp = i * j
    return freq[n]
复制代码

2. 官方题解

2.1 动态规划

f[i] 表示打印出iA的最少操作次数。

对于i来说,应先具有jA,使用一次copy操作,再使用若干次paste操作,得到i

因此j应当时i的因数,粘贴的次数为i/j - 1

状态转移方程: f[i] = min(f[j] + i / j) j是i的因数

def minSteps(n): 
    freq = [0] * (n + 1)
    for i in range(2, n + 1):
        freq[i] = float("inf")
        j = 1
        while j * j <= i:
            if i % j == 0:
                # 状态转移
                freq[i] = min(freq[i], freq[i // j] + j)
                freq[i] = min(freq[i], freq[j] + i // j)
            j += 1
    return freq[n]
复制代码

2.2 质因数分解

上面动态规划的状态转移方程如下: f[i] = min(f[j] + i / j) (j是i的因数)

也等价于: f[i] = min(f[i / j] + j) (j是i的因数)

因此问题转化为:给定初始值n,我们希望通过若干次操作将其变为 1。每次操作我们可以选择n的一个大于 1 的因数j,耗费j 的代价并且将n减少为 n / j。在所有可行的操作序列中,总代价的最小值即为 freq[n]。本质上就是对n进行质因数分解,统计所有质因数的和。

def minSteps(n): 
    ans = 0
    i = 2
    # 分解质因数
    while i * i <= n:
        while n % i == 0:
            ans += i
            n //= i
        i += 1

    if (n > 1):
        ans += n
    return ans
复制代码
文章分类
前端
文章标签