一起刷LeetCode——只有两个键的键盘(动态规划)

58 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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)
};

总结

  • 把复制和粘贴操作的中间结果剪切板数据放到状态的一部分,粘贴操作的状态转移分析就容易了很多
  • 今天也是有收获的一天