最高的塔,按给定顺序堆放盒子

时间:2016-02-29 01:05:46

标签: c++ dynamic-programming

给出N个盒子。我怎样才能找到按照给定顺序用它们制造的最高塔? (给定顺序意味着第一个盒子必须位于塔的底部,依此类推)。 必须使用所有方框来制作有效的塔

可以在任何轴上旋转盒子,使其6个面中的任何一个面平行于地面,但是这样的面的周长必须完全限制在它下面的盒子的上面的周边内。 。在第一个盒子的情况下,可以选择任何面部,因为地面足够大。

为了解决这个问题,我尝试了以下方法:
- 首先,代码为每个矩形生成旋转(只是尺寸的排列)
- 其次为每个盒子和每个可能的旋转构建动态编程解决方案
- 最后搜索最高的塔(在dp表中)

但我的算法在未知的测试用例中采用了错误的答案。这有什么问题?动态编程是解决这个问题的最佳方法吗?

这是我的代码:

#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <cstring>

struct rectangle{
    int coords[3];
    rectangle(){ coords[0] = coords[1] = coords[2] = 0; }
    rectangle(int a, int b, int c){coords[0] = a; coords[1] = b; coords[2] = c; }
};

bool canStack(rectangle &current_rectangle, rectangle &last_rectangle){
    for (int i = 0; i < 2; ++i)
        if(current_rectangle.coords[i] > last_rectangle.coords[i])
            return false;
    return true;
}

//six is the number of rotations for each rectangle
int dp(std::vector< std::vector<rectangle>  > &v){
    int memoization[6][v.size()];
    memset(memoization, -1, sizeof(memoization));

    //all rotations of the first rectangle can be used
    for (int i = 0; i < 6; ++i) {
        memoization[i][0] = v[0][i].coords[2];
    }

    //for each rectangle
    for (int i = 1; i < v.size(); ++i) {
        //for each possible permutation of the current rectangle
        for (int j = 0; j < 6; ++j) {
            //for each permutation of the previous rectangle
            for (int k = 0; k < 6; ++k) {
                rectangle &prev = v[i - 1][k];
                rectangle &curr = v[i][j];
                //is possible to put the current rectangle with the previous rectangle ?
                if( canStack(curr, prev) ) {
                    memoization[j][i] = std::max(memoization[j][i], curr.coords[2] + memoization[k][i-1]);
                }
            }
        }
    }

    //what is the best solution ?
    int ret = -1;
    for (int i = 0; i < 6; ++i) {
        ret = std::max(memoization[i][v.size()-1], ret);
    }
    return ret;
}

int main ( void ) {
    int n;
    scanf("%d", &n);

    std::vector< std::vector<rectangle>  > v(n);

    for (int i = 0; i < n; ++i) {
        rectangle r;
        scanf("%d %d %d", &r.coords[0], &r.coords[1], &r.coords[2]);

        //generate all rotations with the given rectangle (all combinations of the coordinates)
        for (int j = 0; j < 3; ++j)
            for (int k = 0; k < 3; ++k)
                if(j != k) //micro optimization disease
                    for (int l = 0; l < 3; ++l)
                        if(l != j && l != k)
                            v[i].push_back( rectangle(r.coords[j], r.coords[k], r.coords[l]) );

    }

    printf("%d\n", dp(v));
}

输入说明

  

测试用例以整数N开头,表示方框数(1≤N≤10^ 5)。   接下来将有N行,每行包含三个整数,A,B和C,表示方框的尺寸(1≤A,B,C≤10^ 4)。

输出说明

  

打印包含一个整数的行,如果可以堆叠所有N个框,则表示堆栈的最大高度,否则为-1。

示例输入

2
5 2 2
1 3 4

示例输出

6

给定输入和输出的示例图像。

Sample image

2 个答案:

答案 0 :(得分:1)

如果您被允许,此问题可能会受益于树数据结构。

首先,定义块的三种可能情况:

1)立方体 - 只有一种可能的方向选择,因为每个方向都会产生相同的高度(应用于总高度)和相同的占地面积(适用于每个方块的占地面积完全由在它下面阻止)。

2)方形矩形 - 这个矩形有三种可能的方向,两个相等的尺寸(例如,4x4x1或4x4x7都适合这个)。

3)所有不同的尺寸 - 这种形状有六种可能的取向,每一面都与其他面不同。

对于第一个框,选择其形状允许的方向数,并在第一级创建相应的节点(高度为零的根节点将允许使用简单的二叉树,而不是需要更复杂的允许多个树的树类型每个节点内的元素)。然后,对于每个方向,选择下一个框允许的方向数,但只为那些对当前方框的给定方向有效的方向创建节点。如果给定当前框的方向没有可能的方向,则删除整个唯一的方向分支(具有多个有效方向的第一个父节点将通过此修剪删除一个方向,但该父节点及其所有祖先将被保留否则)。

通过执行此操作,您可以通过检查根节点下是否有任何元素来检查没有解决方案的方框集,因为空树表示所有可能的方向已​​被无效组合修剪掉。

如果树不是空的,那么只需走树,找到树的每个分支内的最高高度,递归地向上到树的根 - 总和值是你的最大高度,例如下面的伪代码:

std::size_t maximum_height() const{
    if(leftnode == nullptr || rightnode == nullptr)
        return this_node_box_height;
    else{
        auto leftheight = leftnode->maximum_height() + this_node_box_height;
        auto rightheight = rightnode->maximum_height() + this_node_box_height;
        if(leftheight >= rightheight)
            return leftheight;
        else
            return rightheight;
    }
}

使用树数据结构的好处是

1)您将大大减少必须存储和检查的可能组合的数量,因为在树中,无效的方向将在尽可能早的时候消除 - 例如,使用2x2x5第一个框,有三个可能方向(作为方形矩形),只有两个方向是可能的,因为没有可能的方法将其定位在其2x2端并且仍然适合4x3x1块。如果平均每个块只能有两个方向,那么与计算每个可能的方向相比,您需要的节点数要少得多,然后再过滤它们。

2)检测没有解决方案的块集更容易,因为数据结构只包含有效的组合。

3)使用完成的树将更容易 - 例如,要找到最高的方向序列,而不仅仅是实际高度,您可以将空的std :: vector传递给修改后的最高()实现,除了返回高度之外,让它在追踪树时附加每个最高节点的实际方向。

答案 1 :(得分:1)

通常,您会获得导致您失败的测试用例。否则,找到问题要困难得多。

您可以随时从不同角度接近它!我要省去容易复制的无聊部分。

struct Box { unsigned int dim[3]; };

Box将存储每个...框的尺寸。当需要阅读维度时,需要对其进行排序,以便dim[0] >= dim[1] >= dim[2]

这个想法是每次迭代循环并读取下一个框。然后,它将新框的第二大尺寸与最后一个框的第二大尺寸进行比较,并与第三大尺寸相同。如果在任何一种情况下较新的框都较大,则调整较旧的框以比较第一个和第三个最大尺寸。如果那也失败了,那么第一和第二大。这样,它总是更喜欢使用更大的尺寸作为垂直尺寸。

如果必须旋转一个方框,它会转到下一个方框并检查旋转是否也需要在那里进行调整。它一直持续到没有更多的盒子或者它不需要旋转下一个盒子。如果在任何时候,一个盒子的所有三个旋转都不能使它足够大,它就会因为没有解决方案而停止。

一旦所有方框都到位,它就会总结每个方框的垂直尺寸。

int main()
{
    unsigned int size; //num boxes
    std::cin >> size;

    std::vector<Box> boxes(size); //all boxes
    std::vector<unsigned char> pos(size, 0); //index of vertical dimension

    //gets the index of dimension that isn't vertical
    //largest indicates if it should pick the larger or smaller one
    auto get = [](unsigned char x, bool largest) { if (largest) return x == 0 ? 1 : 0; return x == 2 ? 1 : 2; };

    //check will compare the dimensions of two boxes and return true if the smaller one is under the larger one
    auto check = [&boxes, &pos, &get](unsigned int x, bool largest) { return boxes[x - 1].dim[get(pos[x - 1], largest)] < boxes[x].dim[get(pos[x], largest)]; };

    unsigned int x = 0, y; //indexing variables
    unsigned char change; //detects box rotation change
    bool fail = false; //if it cannot be solved
    for (x = 0; x < size && !fail; ++x)
    {
        //read in the next three dimensions
        //make sure dim[0] >= dim[1] >= dim[2]
        //simple enough to write
        //mine was too ugly and I didn't want to be embarrassed

        y = x;
        while (y && !fail) //when y == 0, no more boxes to check
        {
            change = pos[y - 1];
            while (check(y, true) || check(y, false)) //while invalid rotation
            {
                if (++pos[y - 1] == 3) //rotate, when pos == 3, no solution
                {
                    fail = true;
                    break;
                }
            }
            if (change != pos[y - 1]) //if rotated box
                --y;
            else
                break;
        }
    }
    if (fail)
    {
        std::cout << -1;
    }
    else
    {
        unsigned long long max = 0;
        for (x = 0; x < size; ++x)
            max += boxes[x].dim[pos[x]];
        std::cout << max;
    }
    return 0;
}

它适用于我写过的测试用例,但考虑到我不知道是什么导致了你的失败,我无法告诉你我的做法有何不同(假设它也没有#&# 39; t你的测试条件不合格。)