#include <bits/stdc++.h>
#define For(i, a, b) for(int (i)=(int)(a); (i)<(int)(b); ++(i))
#define rFor(i, a, b) for(int (i)=(int)(a)-1; (i)>=(int)(b); --(i))
#define rep(i, n) For(i, 0, n)
#define rrep(i, n) rFor(i, n, 0)
#define fi first
#define se second
using namespace std;
typedef long long lint;
typedef unsigned long long ulint;
typedef pair<int, int> pii;
typedef pair<lint, lint> pll;
template<class T> bool chmax(T &a, const T &b){if(a<b){a=b; return true;} return false;}
template<class T> bool chmin(T &a, const T &b){if(a>b){a=b; return true;} return false;}
template<class T> T div_floor(T a, T b){
    if(b < 0) a *= -1, b *= -1;
    return a>=0 ? a/b : (a+1)/b-1;
}
template<class T> T div_ceil(T a, T b){
    if(b < 0) a *= -1, b *= -1;
    return a>0 ? (a-1)/b+1 : a/b;
}

constexpr lint mod = 1000000007;
constexpr lint INF = mod * mod;
constexpr int MAX = 100010;

struct edge{
    int to, cap, cost, rev;
    edge(int to, int cap, int cost, int rev): to(to), cap(cap), cost(cost), rev(rev){}
};

int n, V;
vector<edge> G[210];
int h[210], dist[210], prevv[210], preve[210];

void add_edge(int from, int to, int cap, int cost){
    G[from].emplace_back(to, cap, cost, G[to].size());
    G[to].emplace_back(from, 0, -cost, G[from].size() - 1);
}

int min_cost_flow(int s, int t, int f){
    int res = 0;
    fill(h, h + V, 0);
    while(f > 0){
        priority_queue<pii, vector<pii>, greater<>> que;
        fill(dist, dist + V, mod);
        dist[s] = 0;
        que.emplace(0, s);
        while(!que.empty()){
            auto p = que.top();
            que.pop();
            int v = p.se;
            if(dist[v] < p.fi) continue;
            for(int i = 0; i < G[v].size(); ++i){
                edge &e = G[v][i];
                if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){
                    dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    que.emplace(dist[e.to], e.to);
                }
            }
        }
        if(dist[t] == mod) return -1;
        for(int v = 0 ; v < V; ++v) h[v] += dist[v];

        int d = f;
        for(int v = t; v != s; v = prevv[v]){
            d = min(d, G[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += d * h[t];
        for(int v = t; v != s; v = prevv[v]){
            edge &e = G[prevv[v]][preve[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
    return res;
}

void solve(){
    V = 2 * n + 2;
    array<int, 3> cube[n];
    rep(i, n)rep(j, 3) scanf("%d", &cube[i][j]);
    rep(i, 2 * n + 2) G[i].clear();
    rep(i, n){
        rep(j, n)if(i != j){
            sort(cube[i].begin(), cube[i].end());
            do{
                if(cube[i][0] < cube[j][0] && cube[i][1] < cube[j][1] && cube[i][2] < cube[j][2]){
                    add_edge(i, j + n, 1, 0);
                    break;
                }
            }while(next_permutation(cube[i].begin(), cube[i].end()));
        }
    }
    rep(i, n){
        add_edge(i, 2 * n + 1, 1, cube[i][0] * cube[i][1] * cube[i][2]);
        add_edge(2 * n, i, 1, 0);
        add_edge(i + n, 2 * n + 1, 1, 0);
    }
    printf("%d\n", min_cost_flow(2 * n, 2 * n + 1, n));
}

int main(){
    while(scanf("%d", &n) && n){
        solve();
    }
}


Measured memory_bytes: 3380