题目大意:
有n中长方体,每种都有无穷多个。要求选一些长方体搭成尽量高的柱子,使得每个长方体底面的长宽严格小于它下方的长方体(长的跟金字塔差不多)。注意不是小等于!!并且一个长方体有三个参数,每个参数都可以当做是高。
题目解析:
这是最优子结构问题,从一开始就要考虑如何塔的最高。在任何时候只有当前顶面的尺寸会影响到接下来的决策。因此可以用一个二维数组d(i,j)表示当前顶面的长和宽。但因为长和宽可能会非常的大,导致数组开不下,所以我们要间接表示这个状态。
从问题分析,我们发现一个长方体的高有三种可能,也就是说可以把一个长方体分成三种形态。如果我们一开始就用一个二维数组block[i][k]存取第i个长方体的三个参数,那么我们可以用d(idx,j)来表示第idx个长方体的第j种形态,而长宽就对应r[i]中除了第j个元素的另外两个元素。这样数组的大小大大减小。
状态总数是O(n)个,决策也是O(n)个,时间复杂度为O(n^2)。
状态转移方程:
d(i)(j) = max(d(i)(j) , block[i][j] + dp(x,y))。
因为递推太复杂,所以采用记忆化搜索的方法。
代码:
#include <set>
#include <numeric>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cctype>
#include <string>
#include <sstream>
#include <map>
#include <functional>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
#define REP(idx1,num1) for(int idx1=0;idx1<(num1);idx1++)
#define pb push_back
#define empb emplace_back
#define mp make_pair
#define mem(s) memset(s,0,sizeof(s));
const double EPS = 1e-6;
//const int maxn = 280;
int d[33][5];
int n;
int block[33][4];
void get_b(int *v,int i,int h)//获取长宽
{
int ptr = 0;
for(int j = 0; j < 3; ++j)
if(j != h)
v[ptr++] = block[i][j];
}
int dp(int x,int y)
{
if(d[x][y] > 0) return d[x][y];
d[x][y] = block[x][y];//初始化。
int v1[2];//当前顶面的长宽
get_b(v1,x,y);
for(int i = 0; i < n; ++i)
for(int j = 0; j < 3; ++j)
{
int v[2];//将要塔的长方体的长宽
get_b(v,i,j);
if(v[0] < v1[0] && v[1] < v1[1])
d[x][y] = max(d[x][y], block[x][y]+dp(i,j));
else if(v[1] < v1[0] && v[0] < v1[1])
d[x][y] = max(d[x][y], block[x][y]+dp(i,j));
}
return d[x][y];
}
int main()
{
int kase = 1;
while(cin >> n && n){
mem(d);
for(int i = 0; i < n; ++i){
for(int j = 0; j < 3; ++j)
cin >> block[i][j];
//sort(block[i],block[i]+3);
//如果这里进行排序的话上面的状态转移方程就可以少写一个。
}
//枚举所有的长方体
int tallest = -1;
for(int i = 0; i < n; ++i)
for(int j = 0; j < 3; ++j)
tallest = max(tallest,dp(i,j));
printf("Case %d: maximum height = %d\n",kase++,tallest);
}
return 0;
}