OJ链接:https://vjudge.net/problem/UVA-437
题意:
有N种不同大小的方块,每种方块的数量不限,并且可以任意翻转,要用它们堆成尽可能高的塔,要求每层只能有一个方块,并且方块的底面长宽严格小于它下层的方块的长宽。
输入给出:方块种类数N,每种方块的三维(xi,yi,zi)。
输出给出:堆成的塔的最大高度。
解析:
 要求相邻方块的底面长宽是严格小于关系,并且可以任意翻转,所以虽然每种块的数量不限,但实际上只能最多有3个,分别是其3个不同大小的面分别作为底面的情况。可以在输入时直接处理
算法:
动态规划:设置dp数组记录以每一种类型的方块作为最顶层方块时的最大高度,用一个循环遍历所有类型的方块,最后求出最大值。可以用dp[i][j](其中i表示长,j表示宽)来标记每种类型的方块,但是会遇到两个问题,一是输入的x,y没有给定范围,数组大小不能确定,二是可能遇到x,y相同但h不同的方块,会导致dfs遍历时直接返回,而忽略了某些未求解的方块。
所以,这里采用dp[i]来标记方块,其中i表示方块的序号,由于输入时包含了所有类型的方块,因此dp[i]不会漏掉方块,并且i与N成正比,数组大小可以确定
状态转移方程:dp[i] = max(dp[ki])+box[i].h
其中[ki]为每个满足严格小于条件的方块,box[i].h为方块i的高。
代码:
#include<iostream>
#include<string.h>
using namespace std;
struct Box{
int x, y, z;
}box[100];
int dp[100],n,num=0;
int dfs(int i)
{
if(dp[i]>0)
return dp[i]; //已经被搜索过的方块,直接返回结果
int maxn = 0;
for (int k = 0; k < 3*n;++k)
if ((box[k].x<box[i].x&&box[k].y<box[i].y)||(box[k].x<box[i].y&&box[k].y<box[i].x))//判断是否满足条件
{
int tmp = dfs(k); //找到最大值
if (maxn < tmp)
maxn = tmp;
}
return dp[i] = maxn + box[i].z;
}
int main()
{
while(cin>>n&&n)
{
memset(box, 0, sizeof(box));
memset(dp, 0, sizeof(dp));
for (int i = 0; i < 3 * n; i += 3) //处理输入,将一个类型方块的3种底面转换为3个类型的方块
{
cin >> box[i].x >> box[i].y >> box[i].z;
box[i + 1].x = box[i + 2].y = box[i].z;
box[i + 1].y = box[i + 2].z = box[i].x;
box[i + 1].z = box[i + 2].x = box[i].y;
}
int tmp = 0, maxn = 0;
for (int k = 0; k < 3*n;++k)
{
memset(dp, 0, sizeof(dp));
tmp = dfs(k); //循环遍历所有方块,找到作为顶层的最优者
if(maxn<tmp)
maxn = tmp;
}
cout << "Case "<<++num<<": maximum height = " <<maxn << endl;
}
return 0;
}