如何获得堆放箱子的最大高度?

How to get maximum height of piled boxes?

有一组 20 个框,参数为 {height, length, width}。 如果 {length_of_first, width_of_first} 小于或等于 {length_of_second, width_of_second},则一个盒子可以堆叠在另一个盒子上,即

length_of_first <= length_of_second && width_of_first <= width_of_second。 盒子可以在任何一侧旋转

BoxSizes rotateRight(const BoxSizes& box) {
    BoxSizes box_sizes(box);

    int height = box_sizes.height;
    int width = box_sizes.width;
    int length = box_sizes.length;

    box_sizes.length = width;
    box_sizes.height = length;
    box_sizes.width = height;

    return box_sizes;
}

BoxSizes rotateOnward(const BoxSizes& box) {
    BoxSizes box_sizes(box);

    int height = box_sizes.height;
    int width = box_sizes.width;
    int length = box_sizes.length;

    box_sizes.length = height;
    box_sizes.height = length;
    box_sizes.width = width;

    return box_sizes;
}

主要目标是获得最高的箱子堆。 不强制使用所有盒子,但一个盒子必须只使用一次! 例子。给定两个盒子:

{27, 24, 35},
{76, 36, 3}

我可以堆两个箱子,但最好不要堆,拿第二个箱子旋转它得到76

我应该用什么方法来解决这个问题因为我不能使用bruteforce因为我需要考虑每个盒子的3旋转所以我需要运行 (3 + 1) ^ 20 例(+1 因为一个盒子可能用不到)

我的建议是采用一种混合方法,使用动态规划生成堆高上限,然后使用 A* 搜索来找到解决方案。

首先,让我们构建一个具有 60 个节点的图表,表示刚刚将 20 个盒子中的一个放在 3 个方向中的每个方向上。为空堆再添加一个。

如果B可以堆在A上,你的箭就是A -> B。“可以堆”意味着它符合你的堆放规则,还有以下额外规则。如果长度和宽度匹配,则只能将较短的堆放在较长的上面。如果框尺寸相同,则执行任意顺序。

这些限制的原因是,如果有一个有效答案不遵循这些附加规则,您可以重新排列框以找到符合这些规则的相同高度之一。所以我们只需要寻找遵循这些规则的安排。有了这些循环,我们的图中就没有循环了。

接下来,通过简单的递归图搜索,对于每个有方向的框,我们可以找到可以在其上堆放的最高堆,忽略重复规则。记录下那61个高度。

这些高度高估了您可以建造多高的桩。但我们并没有试图强制执行关于永远不重复使用盒子的规则。让我们用 A* 搜索来解决这个问题。

我们首先需要一个 Priority Queue 那个 returns 最大的优先级。 (最大堆会做得很好。)这样,我们的搜索将按照以下伪代码进行。

queue.add((max height on empty ground, 0, empty path))
while True: # loop condition terminates at end.
    (priority, height, path) = queue.pop()
    if priority = height:
        EXIT HERE, HEIGHT IS ANSWER AND PATH IS OPTIMAL PILING
    else:
        queue.add((height, height, path))
        for oriented box that can pile on last in path: # in graph above
            if box not in path:
                new_height = height + height of box
                queue.add((
                    new_height + max_height on orientation,
                    new_height,
                    path with orientation added))

这将做的是从一条空路径开始,将每个方向放入队列,然后贪婪地探索尝试你第一次找到的最高桩,每当它遇到重复的盒子标准时回溯。我们得到的优先级永远是最高堆的高度的上限。高度是到目前为止这堆的高度。这就是为什么当我们找到一个实际高度与优先级匹配的堆时,这就是最佳答案。

正如“btilly”先生所说,我们应该根据方框的所有可能旋转创建一个图表。

每个节点代表一个旋转的(或原始形状)框。

只有当第二个盒子可以堆在第一个盒子上时,节点之间的边才能存在。

那么,让我们介绍一个描述盒子形状的结构:

struct BoxSizes {
    int length{};
    int width{};
    int height{};
    int type{-1};
    int num_of_box{};

    BoxSizes(const BoxSizes&) = default;
    BoxSizes(const std::vector<int>& box, int type, int num_of_box) : length(box[0]), width(box[1]), height(box[2]), type(type), num_of_box(num_of_box) {}
    /*Some helpful methods*/

我们给出这样的盒子形状:

std::vector<std::vector<int>> box_sizes = {
            {269, 152, 375},
            {45, 8, 259},
            {37, 113, 98},
            {685, 40, 35}
};

让我们创建盒子的所有旋转:

std::vector<BoxSizes> all_rotated_box;

for (const auto& box_as_vector : box_sizes) {
     /*Don't pay attention on type and count*/
     const auto& box = BoxSizes(box_as_vector, type++, count);
     count += 3;
     all_rotated_box.emplace_back(box);
     all_rotated_box.emplace_back(rotateRight(box));
     all_rotated_box.emplace_back(rotateOnward(box));
}

/*Let's sort them!*/
std::sort(all_rotated_box.begin(), all_rotated_box.end());

那么,我们来介绍一个结构Node

struct Node {
        int x, y, z;
        int type;
        Node() = default;
        Node(int _x, int _y, int _z, int type) : x(_x), y(_y), z(_z), type(type) {}
        bool operator==(Node a) {
            return (x == a.x) && (y == a.y);
        }
};

std::vector<Node> nodes;

/*From vector of boxes to vector of nodes*/
std::transform(all_rotated_box.begin(), all_rotated_box.end(), std::back_inserter(nodes),
                   [](const auto& box) { return  Node(box.length, box.width, box.height, box.type); });

让我们创建一个图表:

std::vector<std::vector<int>> Graph(nodes.size(), std::vector<int>(nodes.size(), 0));

    for (int i = 0; i < nodes.size(); i++) {
        auto x = nodes[i].x;
        auto y = nodes[i].y;
        auto type = nodes[i].type;

        for (int j = 0; j < nodes.size(); j++) {
            auto x_ = nodes[j].x;
            auto y_ = nodes[j].y;
            auto type_ = nodes[j].type;

            if(i == j) {
                Graph[i][j] = nodes[i].z;
            }

            if(type == type_ || (x == x_ && y == y_))
                continue;

            if((x < x_ || x <= x_) && (y < y_ || y <= y_) ||
               (x < y_ || x <= y_) && (y < x_ || y <= x_)) {
                Graph[i][j] = std::abs( nodes[j].z);
            }

        }
    }

所以,主循环是:

for(auto j = 0; j < nodes.size(); j++) {
        std::deque<int> q;

        q.push_back(j);

        std::vector<int> heighest(nodes.size(), 0);
        heighest[j] = Graph[j][j];

        while (!q.empty()) {
            std::vector<int> adjacent_nodes;

            auto curr{q.front()};
            q.pop_front();

            for (int i = 0; i < Graph[0].size(); i++) {
                if (Graph[curr][i] > 0 && i != curr) {
                    adjacent_nodes.push_back(i);
                }
            }

            for (auto node : adjacent_nodes) {
                heighest[node] = std::max(heighest[node], heighest[curr] + Graph[curr][node]);
                q.push_back(node);
            }
        }

        the_heighest = std::max(the_heighest, *std::max_element(heighest.begin(), heighest.end()));
    }

你可以在这里查看https://github.com/Ayrtat/codeforces/blob/master/BoxRight.cpp