1 题意
给定
n
n
n种木块,每种木块由(长,宽,高)标识,并且有无限多个。现要求层叠木块(可以选择任意一面作为底面),上面木块的底面的长和宽要严格小于下面木块。问:最多可以层叠多高。
链接:link。
2 思路
2.1 动态规划
由于木块是无限的,所以可以考虑木块的所有的摆放状态(每个木块有3种摆放状态)。按底面积对木块从小到大排序,这样状态转移的时候就只需要在前面寻找一个合适的木块(因为底面积大的木块是肯定不能放在上面的)。
状态:
d
p
[
i
]
dp[i]
dp[i]表示把第
i
i
i个木块放在底部最多能层叠多高。
转移:
d
p
[
j
]
=
max
i
<
j
∧
x
i
<
x
j
∧
y
i
<
y
j
(
d
p
[
i
]
)
+
z
j
dp[j]= \max_{i<j \wedge x_{i}<x_{j} \wedge y_{i}<y_{j}}(dp[i])+z_{j}
dp[j]=maxi<j∧xi<xj∧yi<yj(dp[i])+zj
2.1.1 时间复杂的分析
由状态转移过程可知,时间复杂度为 O ( n 2 ) \mathcal{O}(n^{2}) O(n2)。
2.1.2 实现
#include<iostream>
#include<vector>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e2+10;
struct Rec{
int x,y,z;
Rec(int a,int b,int c):x(a),y(b),z(c){}
};
int n,k=0,dp[N];
vector<Rec> vec;
bool operator<(Rec& a,Rec& b){
return a.x*a.y<b.x*b.y;
}
bool cmp(Rec& a,Rec& b){
return a.x>b.x&&a.y>b.y||a.x>b.y&&a.y>b.x;
}
int main(){
while(~scanf("%d",&n)&&n){
vec.clear();
for(int i=0;i<n;i++){
int x,y,z;scanf("%d %d %d",&x,&y,&z);
vec.push_back(Rec(x,y,z));
vec.push_back(Rec(z,y,x));
vec.push_back(Rec(x,z,y));
}
sort(vec.begin(),vec.end());
memset(dp,0,sizeof(dp));
int mx=0;
for(int i=0;i<vec.size();i++){
dp[i]=vec[i].z;
for(int j=0;j<i;j++){
if(cmp(vec[i],vec[j])){
dp[i]=max(dp[i],dp[j]+vec[i].z);
mx=max(dp[i],mx);
}
}
}
printf("Case %d: maximum height = %d\n",++k,mx);
}
return 0;
}