题目大意
有n(n<=30)种长方体,每种都有无穷多个。要求选一些长方体摞成一根尽量高的柱子(可以自行选择一条边作为高),使得每个长方体的底面长宽分别严格小于它下方立方体的地面长宽。
解题思路
这是一个DAG(有向无环图)模型。每种长方体将长宽高全排列变成6个长方体,这就够了,因为题目的限制,这六个长方体最多被使用一次。定义dp[i]表示以第i个长方体为顶时柱子的最大高度。对于每个长方体Ai,遍历其他长方体Bj,如过Bj能成为Ai的顶,并且当Bj放在Ai上时dp[j]变大,那么更新dp[j]。
注意:我们需要事先对所有长方体进行排序,这样才能保证当i更新完j之后,dp[j]的值是j以0~i这些长方体为底时的最优解,这样才能保证j去更新其他长方体时也能使其他长方体得到最优解。
参考代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
struct Block{
int x,y,z;
};
vector<Block> vec;
int dp[1005]; //表示以第i块砖为顶的柱子最大高度
bool isOk(Block a,Block b){ //判断b是否能放在a上面
return (a.x>b.x&&a.y>b.y);
}
bool cmp(Block a,Block b){
if(a.x==b.x)
return a.y>b.y;
else return a.x>b.x; //以x为主降序排列
}
int main(){
int n,x,y,z,t=0;
while (cin >> n,n){
vec.clear();
memset(dp,0,sizeof(dp));
for(int i=0; i<n; i++){
cin >> x >> y >> z;
vec.push_back(Block{x,y,z});
vec.push_back(Block{x,z,y});
vec.push_back(Block{y,x,z});
vec.push_back(Block{y,z,x});
vec.push_back(Block{z,x,y});
vec.push_back(Block{z,y,x});
}
int Max=0;
sort(vec.begin(),vec.end(),cmp);
for(int i=0; i<vec.size(); i++){
dp[i]=vec[i].z;
Max=max(Max,dp[i]);
}
for(int i=0; i<vec.size(); i++){
for(int j=i+1; j<vec.size(); j++){
Block a=vec[i],b=vec[j];
if(isOk(a,b)&&dp[j]<dp[i]+b.z){ //当b能放在a上面且这样以b为顶的柱子更高
dp[j]=dp[i]+b.z;
Max=max(Max,dp[j]);
}
}
}
printf("Case %d: maximum height = %d\n",++t,Max);
}
return 0;
}