破解编码面试第5版,9.10

时间:2015-09-16 03:54:52

标签: java oop recursion dynamic-programming

在Cracking the Coding Interview(第5版)中,提出了以下问题:

你有一堆n个框,宽度为w(i),h(i)和d(i)。如果堆栈中的每个盒子的宽度,高度和深度都严格大于其上方的盒子,则盒子不能旋转并且只能堆叠在一起。实现一种方法来构建最高的堆栈,其中堆栈的高度是每个盒子高度的总和。

我想出了以下不涉及任何动态编程的递归解决方案:

 public static List<Box> stackBoxes(List<Box> boxes){
    if(boxes.size() <= 1){
        return boxes;
    }

    List<Box> temp = new ArrayList<Box>();
    temp = stackBoxes(boxes.subList(1, boxes.size()));
    Box currentBox = new Box(0, 0, 0);
    currentBox = boxes.get(0);

    for(int i = 0; i < temp.size(); i++){
        Box nextBox = new Box(0,0,0);
        nextBox = temp.get(i);
        if(nextBox.x >= currentBox.x && nextBox.y >= currentBox.y 
                && nextBox.z >= currentBox.z){

            List<Box> half1 = new ArrayList<Box>(temp.subList(0, i));
            half1.add(currentBox);

            List<Box> half2 = new ArrayList<Box>(temp.subList(i,  
            temp.size()));

            half1.addAll(half2);
            return half1;
        }
    }

    List<Box> newStack = new ArrayList<Box>();
    newStack.addAll(temp);
    newStack.add(currentBox);
    return newStack;

}

Box类(x宽度,y高度,z深度):

public class Box {
    public int x;
    public int y;
    public int z;

    public Box(int x, int y, int z){
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

对我来说,似乎总是只有一个盒子的最佳堆叠,因为堆叠中任何给定位置的任何盒子在宽度,高度和深度上都将等于或大于它上面的盒子。基本上,最佳堆叠将是有序列表,其中每个框<=其下的一个。但基于本书中的解决方案和一些在线解决方案,我认为我误解了这个问题并且这是不正确的,所以我希望得到一些帮助。你们中的任何人都可以解释为什么这个问题的解决方案需要寻找每个盒子的可能组合,而不仅仅是生成盒子的有序列表吗?

谢谢!

2 个答案:

答案 0 :(得分:1)

考虑只有三个盒子(在WxDxH中)1x7x2,1x6x2和7x1x5的情况。这些盒子没有严格的排序,因为前两个盒子都不适合第三个盒子。然而,正确的解决方案是第三个框本身(5> 2 + 2)。如果我了解您的算法,它就无法应对这种情况。该问题的解决方案需要支持多个堆栈,并将每个框添加到它可以包含的所有堆栈中。

答案 1 :(得分:1)

您的解决方案似乎不正确。它始终以某种顺序报告包含所有框的列表。如果你有3个盒子[w10,h10,d10],[w5,h5,d5],[w1,h1,d20]那么只有前两个形成最高的堆栈。

如果你试图找到递归解决方案,你会发现它需要指数时间。动态编程是最好的解决方案。它只需要二次时间。

这个想法并不那么难。您只需在顶点使用方框绘制方向图,并且只有当目标顶点中的框可以放在源顶点的框顶部时,才在两个顶点之间添加边。每个顶点的重量是其中框的高度。然后,最佳堆栈对应于此图中具有最大顶点权重总和的路径。这里的关键是访问顶点。所有顶点在开头都应该有“尚未访问”标记。每个顶点中的第二个必要字段是聚合最佳堆栈高度,以此顶点的方框结束。第三个属性是对最佳前一个顶点的反向引用。算法从没有入边的顶点开始。你只需将他们的高度作为最佳高度,并将其标记为已访问。在每个下一步中,您只能访问具有所访问源顶点的所有传入边的顶点。当您访问某些顶点时,您会选择最佳入射边(具有最高聚合高度)。您将当前顶点的高度添加到聚合高度并存储在此顶点中。此外,您还将此顶点的引用存储到最佳路径来自的前一个顶点。最后,您应该从没有外边缘的顶点集中选择最佳顶点。从最佳的最终顶点沿着后向引用返回,您可以重建最佳路径,为您提供最佳堆栈。