一次跳跃最多回溯 n 个楼梯

Posted

技术标签:

【中文标题】一次跳跃最多回溯 n 个楼梯【英文标题】:backtracking n staircases at most k steps in a single jump 【发布时间】:2017-07-26 21:19:00 【问题描述】:

你需要爬一个有 n 级台阶的楼梯,你决定通过跳上台阶来做一些额外的锻炼。一次跳跃最多可以走 k 步。返回你爬楼梯的所有可能的跳跃序列,排序。

我的实现显然给了我错误的答案。

def climbingStaircase(n, k):
    final_res=[]
    final_res.append(CSR(n,k,[]))
    return final_res

def CSR(n,k,res):
    if n == 0:
        return res        
    else:
        for i in range(1,k+1):
            if n-i>=0:
                res.append(i)
                n=n-i
                res=CSR(n,i,res)
        return res

对于 n = 4 和 k = 2,输出应该是

[[1, 1, 1, 1],
 [1, 1, 2],
 [1, 2, 1],
 [2, 1, 1],
 [2, 2]]

实际输出:

[[1,1,1,1,2,1]]

谁能指出我遗漏了哪一部分?

【问题讨论】:

【参考方案1】:

下面的代码中存在一个大问题:您在步数范围内为每个可能性减去步数。

n=n-i
res=CSR(n,i,res)

当你探索完你可以用 1 步跳跃做什么时,你需要回溯并从 same 起点尝试(这个实例的原始值n) 的 2 步跳跃。将代码更改为:

res = CSR(n-i, i, res)

这会在您执行循环时保持n 值不变。

此外,您不能将未来的跳跃限制在您刚刚采取的最大范围内。也更改第二个参数:

res = CSR(n-i, k, res)

这应该会让你动起来。也可以试试这个可爱的debug 博客寻求帮助。至少插入一两条追踪语句,如

print n, k, res

在你的日常工作的顶部。

警告

这不是你的全部麻烦。剩下的最大问题是CSR 只返回一个解决方案:您采取的每一步都附加到 same 列表中。您需要一种方法将完整的解决方案收集为单独的列表; climbingStaircase 中的 append 仅在 CSR 完全完成后执行一次。

您需要通过n==0 识别已完成的解决方案。

调试帮助

这是您的程序版本,其中递归参数已修复,并插入了调试跟踪。

indent = ""

def climbingStaircase(n, k):
    final_res = []
    final_res.append(CSR(n, k, []))
    return final_res

def CSR(n, k, res):
    global indent
    indent += "  "
    print indent, n, k, res
    if n == 0:
        print "SOLUTION", res
    else:
        for i in range(1, k+1):
            if n-i >= 0:
                CSR(n-i, k, res + [i])
    indent = indent[:-2]

print climbingStaircase(4, 2)

注意使用“缩进”来帮助可视化递归和回溯。这里的关键部分是,我没有在全局范围内更新res,而是将其保留为局部变量。我现在也删除了返回值,只是转储以输出找到的解决方案。你可以看看它是如何工作的:

   4 2 []
     3 2 [1]
       2 2 [1, 1]
         1 2 [1, 1, 1]
           0 2 [1, 1, 1, 1]
SOLUTION [1, 1, 1, 1]
         0 2 [1, 1, 2]
SOLUTION [1, 1, 2]
       1 2 [1, 2]
         0 2 [1, 2, 1]
SOLUTION [1, 2, 1]
     2 2 [2]
       1 2 [2, 1]
         0 2 [2, 1, 1]
SOLUTION [2, 1, 1]
       0 2 [2, 2]
SOLUTION [2, 2]
[None]

有了这些东西,我希望您可以跟踪您的逻辑并找出如何在您选择的级别上捕获解决方案序列。

【讨论】:

嗨@Prune 感谢您的快速回答! CSR中的“如果n == 0:返回res”不是识别完整的解决方案吗?是的,我确实得到了一份大清单,而不是清单清单。我试图弹出我添加的最后一项,但还没有成功。 这确实可以识别解决方案,但是您处理不当。您将其传递回上一个实例级别,将其保存在自身之上,然后转到允许范围内的下一个跳转大小,而不将完成的解决方案保存在主列表中。您需要在问题的这个分支中重新考虑您的逻辑:追加完成的解决方案并将部分解决方案重置为上一步级别。 非常感谢您抽出宝贵时间。这可能是我在 SO 中收到的最有帮助 + 体贴的答案。【参考方案2】:

成功实现了 Prune 的答案。

def climbingStaircase(n, k):
    res=[]
    CSR(n,k,[],res)
    return res

def CSR(n,k,str_, res):
    if n == 0:
        res.append(str_)
    else:
        for i in range(1,k+1):
            if n-i>=0:
                CSR(n-i,k,str_+[i],res)

【讨论】:

【参考方案3】:

此解决方案的快速 Java 版本:

int[][] climbingStaircase(int n, int k) 
    List<ArrayList<Integer>> list = new ArrayList<>();
    climb(n, k, new ArrayList<Integer>(), list);

    // convert to int[][]
    int[][] result = new int[list.size()][];
    for (int i=0; i<list.size(); i++) 
        List<Integer> l = list.get(i); 
        int [] arr = new int[l.size()];
        for (int j=0; j<l.size(); j++)
            arr[j] = l.get(j);
        result[i] = arr;
    
    return result;

void climb(int n, int k, ArrayList<Integer> prev, List<ArrayList<Integer>> list) 
    if (n==0)  // no more stairs, done climbing
        list.add(prev);
     else 
        for (int i=1; i<=k; i++)  // climb remaining stairs in intervals from 1 to k steps
            if (i <= n)  // no need to test intervals larger than remaining # of stairs
                ArrayList<Integer> branch = new ArrayList<>(prev);
                branch.add(i);
                climb(n-i, k, branch, list);
            
        
    

【讨论】:

以上是关于一次跳跃最多回溯 n 个楼梯的主要内容,如果未能解决你的问题,请参考以下文章

(递归)3089:爬楼梯

楼梯问题——理解基本逻辑

解题(GoUpstairs -- 上楼梯)

n阶楼梯,一次走1,2,3步,求多少种不同走法

递归----小白上楼梯

走楼梯