#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
// 参考 http://blog.csdn.net/u014800748/article/details/43735015
// 题目中的条件是 n 种, 而例题嵌套矩形中的条件是 n 个
// n 种 block 不限供应,也可以将每种block以3种平面为底看成是不同的block,总共 3n 个 block,不限供应
// 如果 a[0] <= a[1] <= a[2], 每种 block 最多用到两个
// if a[0] <= a[1] <= a[2], at most 2 blocks of the same type will be used, the bottom with area a[0] * a[1], and the above with area a[1] * a[2]
struct block {
int a[3]; // 3 dimensions
};
block blockTypes[30];
int n; // n <= 30
int maxiumHeight;
// n 种 block,每种 block 3 种底面,数组 d[30][3] 遍历各种情况
int d[30][3]; // d[idx][k] is the maxium height when block of type idx is at the bottom, and the height of block is dimension k
// idx is the type number, at most 30 types
// k represents the height of the block. each block has 3 dimentions, k may be 0, 1, or 2.
int kase = 0;
int max(int a, int b)
{
if (a > b) return a;
else return b;
}
/*
compute 2 other dimensions from dimension k
k 0 1 2
(k+1)%3 1 2 0
(k+2)%3 2 0 1
a[]是有序的,但这样计算不能保证另两个dimension有序,比如 k == 1 时
下面这种计算方式可以,如果用这种方式,dp(idx,k) 里面的 if(l < w) 和 if(ll < ww) 以及 (l > ww && w > ll) 都可以去掉
k 0 1 2
(2-k)/2 1 0 0
(3-k)/2 + 1 2 2 1
*/
// 这里的写法是 记忆化搜索
// 不太好用递归,如果用递归,需要有个计算次序,例题硬币问题里从最小的面值开始计算,例题9-1从最后一站向前计算, 这题里面没有明显的顺序
int dp(int idx, int k)
{
if (d[idx][k] > 0) // already computed
return d[idx][k];
int l = blockTypes[idx].a[(k + 1) % 3]; // length
int w = blockTypes[idx].a[(k + 2) % 3]; // width
if (l < w){
int tmp = l; l = w; w = tmp;
}
d[idx][k] = blockTypes[idx].a[k]; // initial value
for (int i = 0; i < n; i++){
for (int j = 0; j < 3; j++){
int ll = blockTypes[i].a[(j + 1) % 3];
int ww = blockTypes[i].a[(j + 2) % 3];
if (ll < ww){
int tmp = ll; ll = ww; ww = tmp;
}
if ((l > ll && w > ww) /*|| (l > ww && w > ll) */) { // 如果去掉上面的 if(l < w) 和 if(ll < ww) 这里的注释要恢复
d[idx][k] = max(d[idx][k], dp(i,j) + blockTypes[idx].a[k]);
maxiumHeight = max(maxiumHeight, d[idx][k]);
}
}
}
return d[idx][k];
}
int main()
{
while (scanf("%d", &n) && n) {
for (int i = 0; i < n; i++) {
scanf("%d%d%d", &blockTypes[i].a[0], &blockTypes[i].a[1], &blockTypes[i].a[2]);
sort(blockTypes[i].a, blockTypes[i].a + 3); // a[0] <= a[1] <= a[2]
}
maxiumHeight = 0;
memset(d, 0, sizeof(d));
for (int i = 0; i < n; i++){
for (int k = 0; k < 3; k++){
dp(i, k);
}
}
printf("Case %d: maxium height = %d\n", ++kase, maxiumHeight);
}
return 0;
}