问题描述
有一组 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
。
我应该用什么方法来解决这个问题 因为我不能使用蛮力,因为我需要考虑每个盒子的 3
旋转,所以我需要运行 (3 + 1) ^ 20
案例(+1
因为可能没有使用盒子)
解决方法
我的建议将是一种混合方法,它使用动态规划来生成桩高的上限,然后通过 A* 搜索来找到解决方案。
首先,让我们构建一个具有 60 个节点的图,代表刚刚将 20 个盒子中的一个放在 3 个方向中的每一个。为空堆再添加一个。
如果 B 可以堆放在 A 上,则您的箭头是 A -> B。“可以堆放”意味着它符合您的堆放规则以及以下额外规则。如果长度和宽度匹配,则只能将较短的堆积在较长的上面。如果盒子尺寸相同,则强制执行任意顺序。
这些限制的原因是,如果有一个不遵循这些附加规则的有效答案,您可以重新排列框以找到一个高度相同的框。所以我们只需要寻找遵循这些规则的安排。有了这些循环,我们的图中就没有循环了。
接下来,通过简单的递归图搜索,对于每个有方向的框,我们可以找到可以在其上放置的最高堆,忽略重复规则。记录这 61 个高度。
这些高度高估了您可以建造多高的桩。但是我们并没有试图强制执行永远不要重复使用一个盒子的规则。让我们用 A* 搜索来解决这个问题。
我们需要一个 Priority Queue 来首先返回最大的优先级。 (最大堆会很好。)有了这个,我们的搜索就变成了下面的伪代码。
queue.add((max height on empty ground,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,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