力扣每日一题:650 只有两个键的键盘
Posted 战场小包
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了力扣每日一题:650 只有两个键的键盘相关的知识,希望对你有一定的参考价值。
题源:只有两个键的键盘
初读这个题,感觉就应该是动态规划,但是找不出状态转移方程,当看到题解之后还是感到非常遗憾,就差一点点。
1. 思路与想法
题目中只有两种操作,copy
和paste
,其中copy
必须拷贝当前的所有内容,最终得出n
个A
所需操作的最小次数。
最小次数,copy
全部,这两个在一开始就给予了我贪心的感觉,对于n
个A
来说,它期望的上一次操作是n/2
处整体复制粘贴过来,n/2
是n/4
…依次类推,就可以得到最小的次数。但很快我发现了上述思想的漏洞,举个例子,如果此时的n
是15
,那就没法通过n/2->n/4
这种模式。
15 = 3 * 5
,15
虽然不能通过上述的模式得来,但可以通过因数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]
表示打印出i
的A
的最少操作次数。
对于i来说,应先具有j
个A
,使用一次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
以上是关于力扣每日一题:650 只有两个键的键盘的主要内容,如果未能解决你的问题,请参考以下文章
力扣650. 只有两个键的键盘 递归法素数分解法与动态规划法多种解法!
LeetCode 292. Nim 游戏(博弈) / 650. 只有两个键的键盘 / 673. 最长递增子序列的个数(LIS二分+前缀和) / 58. 最后一个单词的长度