I have a box stacking-like problem: Given boxes of 3 dimensions find the longest sequence (sorted from largest to smallest box) of boxes so that each box can be nested in another like a nesting doll. We can rotate boxes in multiples of 90°. I solved this problem by connecting each box to every box which can be nested in it and then I applied DFS-like algorithm to find the longest path from infinite large box. But I would like to speed up this algorithm as well as reduce memory using. Any suggestions how can I do it?
#include <iostream>
#include <list>
using namespace std;
struct Box {
int h;
int w;
int l;
};
// Sort box dimensions from smallest to largest so then we can easily decide if some box can fit into another.
Box createBox(int h, int w, int l)
{
if (h > l) {
swap(h, l);
}
if (h > w) {
swap(h, w);
}
if (w > l) {
swap(w, l);
}
struct Box box = { h, w, l };
return box;
}
// Does the second box fit in the first?
bool doesFit(Box box1, Box box2)
{
if (box1.h > box2.h &&
box1.w > box2.w &&
box1.l > box2.l) {
return true;
}
return false;
}
class Graph {
int V;
void findLongestPathUtil(int v, int path[], int& path_index);
vector<Box> boxes;
list<int> longest_path;
public:
Graph(int V);
list<int>* adj;
void addEdge(Box b);
int lpath_index = 0;
void linkVertices();
list<int> findLongestPath(int start);
};
Graph::Graph(int V)
{
this->V = V;
list<int> lpath(V, -1);
longest_path = lpath;
adj = new list<int>[V];
}
void Graph::addEdge(Box b)
{
boxes.push_back(b);
}
// Each directed arc from node A to node B indicates that the corresponding Box A holds Box B
void Graph::linkVertices()
{
for (int i = 0; i < V; i ) {
for (int k = 0; k < V; k ) {
if (doesFit(boxes[i], boxes[k])) {
adj[i].push_back(k);
}
}
}
}
list<int> Graph::findLongestPath(int start)
{
int* path = new int[V];
int path_index = 0;
this->findLongestPathUtil(start, path, path_index);
return this->longest_path;
}
void Graph::findLongestPathUtil(int v, int path[], int& path_index) {
path[path_index] = v;
path_index ;
if (path_index > lpath_index) {
int k = 0;
for (list<int>::iterator i = longest_path.begin(); i != longest_path.end(); i) {
if (k >= path_index)
break;
*i = path[k];
k = 1;
}
lpath_index = path_index;
}
list<int>::iterator i;
for (i = adj[v].begin(); i != adj[v].end(); i) {
findLongestPathUtil(*i, path, path_index);
}
path_index--;
}
int main()
{
int n;
cin >> n;
Graph g(n 1);
for (int i = 0; i < n; i ) {
int w, l, h;
cin >> w >> l >> h;
g.addEdge(createBox(w, l, h));
}
g.addEdge(createBox(INT_MAX, INT_MAX, INT_MAX)); // Infinite box
g.linkVertices();
list<int> path = g.findLongestPath(n);
cout << g.lpath_index-1 << endl; // Subtract one because path has also the infinite box
int k = 0;
for (auto i : path)
{
if (k >= g.lpath_index)
break;
if (k != 0)
cout << i << endl;
k = 1;
}
return 0;
}
To add understanding of the problem I also add example input/output:
10 # number of boxes
8 10 9 # length, width, height of 1. box...
9 7 10
9 10 8
7 7 9
6 1 5
2 6 4
4 5 4
6 7 10
8 6 1
3 10 10
Output:
3 # number of boxes in the nesting doll
0 # index of box in input...
3
4
CodePudding user response:
There's a simpler, faster algorithm for solving this problem:
First, rotate all the boxes so that h
is the longest dimension, then w
, then d
. Then, sort all the boxes by (h,w,d)
. Note that any box in this list can only contain boxes that occur earlier in the list.
For each box, calculate the largest number of boxes that can nest inside, and make a link to the largest box in the largest nesting chain. This is easy to do for each box just by examining the already-calculated results for the smaller boxes that fit inside. This step dominates the time taken by the algorithm, requiring O(N2) time.
Finally, find the box with the longest chain, and follow the links to generate the list of boxes in the chain.