携手创作,共同成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第29天,点击查看活动详情
只有两个键的键盘
最初记事本上只有一个字符
'A'
。你每次可以对这个记事本进行两种操作:
Copy All
(复制全部):复制这个记事本中的所有字符(不允许仅复制部分字符)。Paste
(粘贴):粘贴 上一次 复制的字符。 给你一个数字n
,你需要使用最少的操作次数,在记事本上输出 恰好n
个'A'
。返回能够打印出n
个'A'
的最少操作次数。
分析
- 只用两个操作,复制和粘贴,在记事本上输出恰好n个,求最少的操作次数,对于复制和粘贴操作,需要分析下细节
- 复制操作,记事本输出的还是操作之前的字符,会把操作之前记事本的字符写进剪切板
- 粘贴操作,记事本的输出会新增剪切板的字符
- 因此,在复制粘贴的时候,需要关注的数据有两个部分:一个是记事本输出的数据,一个是剪切板的数据,记事本输出的数据是增长的,剪切板的数据是不断刷新的
- 尝试使用动态规划
- 状态定义dp(a,b,n)表示剪切板字符个数为a并且记事本输出字符个数是b,切好需要n个字符的时候,最小的操作
- 在涉及使用剪切板中数据的操作都要加1,这里的1是复制的操作,毕竟剪切板的数据是来自复制的
- 状态转移:
- 选择复制操作后,剪切板中字符个数为a,输出为b,需要恰好有n个的操作数和选择粘贴操作后,剪切板字符个数为a,输出后是a+b,需要恰好有n个字符的操作数中的最小值,之后加1
- 在状态中使用的是剪切板中数据,这里需要加1是加上一次复制的操作,毕竟剪切板的数据是来自复制操作
代码
var minSteps = function(n) {
let mem[1001][1001]
function dp(a, b, n) {
if(b > n) {
return 100000000
}
if(b === n) {
return 0
}
if(mem[a][b]!== -1) {
return mem[a][b]
}
if(a === 0) {
return 1 + dp(b,b,n)
}
if(a === b) {
return 1 + dp(b,b+a,n)
}
let copy = dp(a,b,n)
let paste = dp(a,b+a, n)
return mem[a][b] = 1+Math.min(copy,paste)
}
if(n === 1) return 0
return 1+dp(1,1,n)
};
总结
- 把复制和粘贴操作的中间结果剪切板数据放到状态的一部分,粘贴操作的状态转移分析就容易了很多
- 今天也是有收获的一天