【问题标题】:Recursion Approach for leetcode 2 keys keyboard problemleetcode 2键键盘问题的递归方法
【发布时间】:2020-06-25 09:42:44
【问题描述】:

我正在尝试使用递归来解决这个问题https://leetcode.com/problems/2-keys-keyboard/。我初始化了 2 个变量屏幕和缓冲区。 screen 表示屏幕上所有字符的计数,buffer 表示上一步复制的所有字符的计数

所以当我们复制时函数是这样调用的 - 功能(屏幕,屏幕)

和粘贴意味着 - 函数(屏幕+缓冲区,缓冲区)

但这不起作用这是代码-

def keyboard(screen,buffer, c,n):
    if screen == n:
        return c
    if screen>n:
        return
    if buffer>n:
        return    
  
      
    keyboard(screen+buffer,buffer,c+1,n)
    keyboard(screen,screen,c+1,n)

print(keyboard(1,0,0,8)) 

我得到了最大的递归深度 这是记忆方法

    def keyboard(self,screen,buffer,c,n,dp):
        
        large_num = 100000 # larger than possible moves
        
        if dp[screen][buffer] != -1:
            return dp[screen][buffer]
  
        if screen == n:
            return c           

        if screen>n:
            return large_num  

        if buffer>n:
            return large_num  

        if c > n:  
            return large_num
       
        dp[screen][buffer] =  min(self.keyboard(screen,screen,c+1,n,dp), self.keyboard(screen+buffer,buffer,c+1,n,dp))
        return dp[screen][buffer]  

    def minSteps(self, n: int) -> int:
        dp = [[-1 for i in range(n+1)] for j in range(n+1)]

        return(self.keyboard(1,0,0,n,dp))

【问题讨论】:

  • 如果递归是问题,请改用循环。
  • 预期输出是什么?
  • 您的keyboard 函数引用了一个不存在的“缓冲区”。 NameError: name 'buffer' is not defined.
  • 您的递归调用keyboard(1, 0, 0, 8) 将无限循环运行。当您添加screen + buffer keyboard(1 + 0, 0, 0, 8) will 时,被称为keyboard(1, 0, 0, 8) 一次又一次。而且在你的代码中也没有提到解决这个问题的条件。

标签: python algorithm recursion dynamic-programming


【解决方案1】:

两个问题:

  • 需要限制移动次数(即c值)
  • 需要通过取最小值来优化动作

代码

def keyboard(screen,buffer,c,n):
  large_num = 100000 # larger than possible moves

  if screen == n:
    return c           # Found solution

  if screen>n:
    return large_num  # Out of bounds (make moves larger than max possible)

  if buffer>n:
    return large_num  # Out of bounds (make moves larger than max possible)

  if c > n:  # limit number of moves
    return large_num
  
  # Optimize choice by taking minimum
  return min(keyboard(screen,screen,c+1,n), keyboard(screen+buffer,buffer,c+1,n))

print(keyboard(1,0,0,8)) 
# Output: 6

添加记忆

def keyboard(screen,buffer,c,n, memcache = None):
  if memcache is None:
    memcache = {}
  large_num = 100000 # larger than possible moves

  # Input state as tuple
  state = (screen, buffer, c, n)

  if state in memcache:
    return memcache[state]

  if screen == n:
    return c           # Found solution

  if screen>n:
    return large_num  # Out of bounds (make moves larger than max possible)

  if buffer>n:
    return large_num  # Out of bounds (make moves larger than max possible)

  if c > n:  # limit number of moves
    return large_num
  
  # Using memoization to cache current value
  # Optimize choice by taking minimum
  memcache[state] = min(keyboard(screen,screen,c+1,n), keyboard(screen+buffer,buffer,c+1,n))

  return memcache[state]

print(keyboard(1,0,0,8)) 

使用装饰器进行记忆

这等效于前一种情况,但使用装饰器来允许记忆任何具有可清洗位置参数的函数,

def memoize(f):
    """This automates the previous example where we added a cache.  
       It uses a decorator function to add a cache to any function with
       hashable position arguments
    """
    memo = {}
    def helper(*args):
        if args not in memo:            
            memo[args] = f(*args)
        return memo[args]
    return helper

@memoize
def keyboard(screen,buffer,c,n):

  large_num = 100000 # larger than possible moves

  if screen == n:
    return c           # Found solution

  if screen>n:
    return large_num  # Out of bounds (make moves larger than max possible)

  if buffer>n:
    return large_num  # Out of bounds (make moves larger than max possible)

  if c > n:  # limit number of moves
    return large_num
  
  # Using memoization to cache current value
  # Optimize choice by taking minimum
  return min(keyboard(screen,screen,c+1,n), keyboard(screen+buffer,buffer,c+1,n))

print(keyboard(1,0,0,8))

修复海报记忆代码

问题是记忆没有超过输入的完整状态。

  • 发布的代码使用(屏幕、缓冲区)作为状态
  • 状态实际上是 (screen, buffer, c)(不需要 n,因为它是固定的)

固定代码

class Solver:
  def keyboard(self,screen,buffer,c,n,dp):
    
    large_num = n + 1 # larger than largest possible moves
    
    if screen>n or buffer>n or c > n:
      return large_num 

    if dp[screen][buffer][c] != -1:
      return dp[screen][buffer][c]

    if screen == n:
      dp[screen][buffer][c] = c          
    
    elif buffer == 0:
      dp[screen][buffer][c] = self.keyboard(screen, screen, c+1, n, dp)
    else:
      # screen can not be zero
      # minimum of copy and paste move
      dp[screen][buffer][c] =  min(self.keyboard(screen,screen,c+1,n,dp), self.keyboard(screen+buffer,buffer,c+1,n,dp))
      
    return dp[screen][buffer][c]  

  def minSteps(self, n: int) -> int:
    dp = [[[-1 for i in range(n+1)] for j in range(n+1)] for k in range(n+1)]

    return self.keyboard(1,0,0,n,dp)

s = Solver()
print(s.minSteps(8))
# Output: 6

【讨论】:

  • 非常感谢你能帮我知道你的方法是什么吗?您是如何得出这样的解决方案的?我做错了什么?
  • @PratyushNarain--1) 您的函数调用键盘两次,表示可以执行的两个操作。但它不会选择这些操作中的哪一个更好。分钟就是这样做的。 2)我们知道解决方案应该少于 n 步,所以我们应该限制 c 到那个。 3) 我们需要返回一个值来帮助我们选择最佳选项(这就是我们在超出范围时返回 large_num 的原因)。
  • @PratyushNarain——另一种思考方式是搜索算法,每轮有两个动作(复制或粘贴)。这个想法是找到以最少的移动次数将您带到目的地的移动顺序。对于副本,我的移动计数将是keyboard(screen+buffer,buffer,c+1,n)。对于粘贴,我的移动计数将是keyboard(screen,screen,c+1,n)。我需要在这两者之间进行选择以获得最小移动计数,即min(keyboard(screen, ...), keyboard(screen+buffer...)
  • 非常感谢。最后一个问题。你能帮我如何使用记忆化来解决这个问题吗?如果你能帮助我,我将不胜感激:)
  • @PratyushNarain--添加了几个记忆化的例子。如果还有其他问题,请告诉我。
猜你喜欢
  • 1970-01-01
  • 2021-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多