Description
有n(n<=30)种立方体,每种都有无穷多个。要求选一些立方体摞成一根尽量高的柱子(可以自行选择哪一边作为高),使得每个立方体的底面长宽分别严格小于它下方立方体的底面长度。
Inout sample
1
10 20 30
2
6 8 10
5 5 5
7
1 1 1
2 2 2
3 3 3
4 4 4
5 5 5
6 6 6
7 7 7
5
31 41 59
26 53 58
97 93 23
84 62 64
33 83 27
0
Output sample
Case 1: maximum height = 40
Case 2: maximum height = 21
Case 3: maximum height = 28
Case 4: maximum height = 342
————————————————分割の线————————————————
分析
根据题意,上方立方体的底面积应当小于下方立方体的底面积,又因为每个立方体有无数个,所以可以得到一条结论
一个立方体下方的摆放不影响其上方的立方体摆放。
由此可以设f[i][j]表示到立方体i时以第j条边作为高所能达到的最高值。(a[i][j]表示第i个立方第j条边的高度)
于是就开始了对于状态转移方程的苦思冥想,
……
……
果然还是用记忆化搜索更简单
代码如下
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int f[2000][3],n;//f[i][j]表示以第i个立方体为底以第j条边为高的最大高度
int a[2000][3],cnt=0;
int dp(int u,int hight)
{
if(f[u][hight]>=0) return f[u][hight];
f[u][hight]=a[u][hight];//高至少为自己
int x,y,ux,uy;//x,y为第u个立方体的长和宽
if(hight==0)
x=a[u][1],y=a[u][2];
if(hight==1)
x=a[u][0],y=a[u][2];
if(hight==2)
x=a[u][0],y=a[u][1];
for(int i=1;i<=n;i++)
for(int k=0;k<3;k++)
{//ux,uy为第i个立方体的长和宽
if(k==0)
ux=a[i][1],uy=a[i][2];
if(k==1)
ux=a[i][0],uy=a[i][2];
if(k==2)
ux=a[i][0],uy=a[i][1];
if(x>ux&&y>uy||y>ux&&x>uy)//如果满足长和宽严格小于的这个基本事实,则向下进行搜索
if(f[u][hight]<dp(i,k)+a[u][hight])
f[u][hight]=a[u][hight]+dp(i,k);//保留最佳答案
}
return f[u][hight];
}
int main()
{
scanf("%d",&n);
while(n>0)//如果n不为0,则再次循环
{
cnt++;//记录循环次数
for(int i=1;i<=n;i++)
scanf("%d%d%d",&a[i][0],&a[i][1],&a[i][2]);//读入三条边
memset(f,-1,sizeof(f));//初始化f,默认f未做过
for(int i=1;i<=n;i++)
for(int k=0;k<3;k++)
{
f[i][k]=dp(i,k);//请完成第一步,剩下的都交给记忆化搜索吧
}
int ans=0;
for(int i=1;i<=n;i++)
for(int k=0;k<3;k++)
if(f[i][k]>ans) ans=f[i][k];//无脑枚举,求最大值
printf("Case %d: maximum height = %d\n",cnt,ans);//按规则输出答案
scanf("%d",&n);//再次读入n
}
return 0;
}