一、概述
1. 问题描述
有不限数量的n种矩形,每种矩形的长宽高用(x,y,z)表示,矩形可以改变放置的形态,即x,y,z可以两两调换,定义矩形r1可以放置到矩形r2上仅当,r1.x<r2.x且r1.y<r2.y,求这n种矩形可以放置的最大高度。
2. 问题链接
HDU -- Monkey and Banana(ACM Step: 3.2.6)
3. 问题截图
图1.1 问题截图
二、算法思路
1. 约定:
输入的矩形表示为:
分别表示第i个矩形的长、宽、高
2. 思路
由于每种矩形可以有6中不同的放置方式,每种方式对应的长宽高如下所示:
(x,y,z),(x,z,y)
(y,x,z),(y,z,x)
(z,x,y),(z,y,x)
所以如果输入的矩形数是n,实际上有6n种矩形可以用来构成结果
问题的结果可以描述为:
假设是得到最大高度的矩形序列,那么问题的结果可以用下述公式表示:
表示输入矩形中x最大和最小的矩形,将上式中的
范围单独分析,可以得到等价的下式:
提出的范围,可以得到:
括号内表示:当是解序列的最后一个矩形,即解序列中长、宽最小的矩形,可以得到的高度最大值,可见,括号内实际上是一个关于
的函数,可以用
表示,则上述公式可以简化为:
因此题目的解答转变成求解数组,并在其中挑出最大值。
现在分析当取不同值时,
之间的关系。
当,此时
,因为当解序列中x(长度)最小的矩形是输入序列中x最大的矩形,此时没有一个矩形可以满足排在它前面的条件,因此这是的
就是x最大节点的z值。
当时,
代表输入序列中x值第二大的矩形,分析此时的
,当x第二大的矩形作为解序列的最后一个矩形时,它能得到的最大值是什么呢?可以发现,首先需要找到满足长宽均大于
的矩形,此时只有
矩形可能满足,可能的意思是
只是x大于
,还需要比较他们的y值。如果都满足,那么此时
表示
可以放到
前面;不满足则
通过分析发现,对于某一个矩形,需要在所有长度大于它的矩形中,通过判断x,y条件,找到最大值,所以可以先将输入矩形序列以长度(x)做递减排列,这样的构造就可以利用之前求出的值。
三、算法实现
#include <iostream> //for cin, cout, endl
#include <algorithm> //for sort
using std::cin;
using std::cout;
using std::endl;
using std::sort;
//hold the input data
struct Data
{
int x;
int y;
int z;
};
const int MAXSIZE = 30 * 6; //max of the number of blocks is 30, each block can combination to 6 different kind
Data data[MAXSIZE];
int data_num; //the number of data elements
long long ans[MAXSIZE]; //ans for answer, hold the result of problem
bool sort_data(Data d1, Data d2)
{
if(d1.x > d2.x)
return true;
else
return false;
}
//get full permutation of x, y, z where the arr pointing to and put it into data array
void get_permutation(int* arr)
{
data[data_num].x = arr[0];
data[data_num].y = arr[1];
data[data_num].z = arr[2];
data_num++;
data[data_num].x = arr[0];
data[data_num].y = arr[2];
data[data_num].z = arr[1];
data_num++;
data[data_num].x = arr[1];
data[data_num].y = arr[0];
data[data_num].z = arr[2];
data_num++;
data[data_num].x = arr[1];
data[data_num].y = arr[2];
data[data_num].z = arr[0];
data_num++;
data[data_num].x = arr[2];
data[data_num].y = arr[0];
data[data_num].z = arr[1];
data_num++;
data[data_num].x = arr[2];
data[data_num].y = arr[1];
data[data_num].z = arr[0];
data_num++;
}
/*
* name: calculate
* description: calculate the result
* return value: void
* argument: n indicate the number of blocks from input
*/
long long calculate(int n)
{
long long ret, tmp;
sort(data, data+n, sort_data); //sort the data to an decreasing order of x
ret = ans[0] = data[0].z;
for(int i=1; i<n; i++){
tmp = 0; //tmp hold the max of previous blocks
//find the max value of block i(ans[i]) of max value of previous blocks(ans[0~i-1]) that meet the contition of x and y
for(int j=0; j<i; j++){
//only meet the x, y condition, the block i can build on the max value of block j
if(data[j].x>data[i].x && data[j].y>data[i].y && ans[j]>tmp)
tmp = ans[j];
}
ans[i] = data[i].z + tmp;
if(ans[i] > ret)
ret = ans[i]; //update the return value
}
return ret;
}
//print the result
void print(long long n)
{
static int num = 0;
num++;
cout << "Case " << num << ": maximum height = " << n << endl;
}
int main()
{
int n;
int tmp[3]; //hold x, y, z
long long res; //res for result, store the return value of function:calculate()
while(cin>>n && n!=0){
data_num = 0;
for(int i=0; i<n; i++){
for(int j=0; j<3; j++)
cin >> tmp[j];
get_permutation(tmp);
}
res = calculate(data_num);
print(res);
}
return 0;
}