#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
#define int ll
typedef pair<int,int> pii;
#define rep(i,n) for(ll i=0;i<(ll)(n);i++)
#define all(a) (a).begin(),(a).end()
#define pb emplace_back
#define INF (1LL<<59)

#define MAX_V (100*2+2)
struct edge { int to, cap, cost, rev;};

vector<edge> G[MAX_V];
vector<int> h(MAX_V);
vector<int> dist(MAX_V);
int prevv[MAX_V], preve[MAX_V];

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

void shortest_path(int s, vector<int> &d){
    int v = d.size();
    rep(i,d.size())d[i]=INF;
    d[s]=0;
    rep(loop,v){
        rep(i,v){
            for(auto e:G[i]){
                if(e.cap && d[i]!=INF && d[e.to]>d[i]+e.cost)
                    d[e.to]=d[i]+e.cost;
            }
        }
    }
}

int min_cost_flow(int s, int t, int f){
    int res = 0;
    shortest_path(s,h);

    while(f>0){
        priority_queue<pii,vector<pii>, greater<pii> >que;
        rep(i,dist.size())dist[i]=INF;
        dist[s] = 0;
        que.push(pii(0,s));
        while(!que.empty()){
            pii p = que.top();
            que.pop();
            int v = p.second;
            if(dist[v]<p.first)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.push(pii(dist[e.to], e.to));
                }
            }
        }
        if(dist[t]==INF)return -INF;

        for(int v=0;v<h.size();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;
}

struct data{
    int x,y,z;

    bool operator < (data obj) const{
        if(x<obj.x && y<obj.y && z<obj.z)return true;
        else return false;
    }
};

int taiseki(int a, int b, int c){
    return a*b*c;
}


/*
 vector<edge> G[MAX_V];
 vector<int> h(MAX_V);
 vector<int> dist(MAX_V);
 int prevv[MAX_V], preve[MAX_V];
 */

signed main(){
    int n;
    while(cin>>n&&n){
        rep(i,MAX_V)G[i].clear();
        rep(i,h.size())h[i] = 0;
        rep(i,dist.size())dist[i] = 0;
        rep(i,MAX_V)prevv[i] = preve[i] = 0;

        vector<data> v(n);
        rep(i,n){
            vector<int> tt(3);
            rep(j,3)cin>>tt[j];
            sort(all(tt));
            v[i] = data{tt[0],tt[1],tt[2]};
        }

        int S = 2*n;
        int T = S+1;

        rep(i,n) add_edge(S  , i  , 1, 0);
        rep(i,n) add_edge(i  , n+i, 1, 0);
        rep(i,n) add_edge(n+i, T  , 1, 0);

        add_edge(S,T,INF,0);

        rep(i,n){
            rep(j,n){
                if(i==j)continue;
                if(v[i]<v[j]){
                    add_edge(j+n, i, 1, -taiseki(v[i].x, v[i].y, v[i].z));
                }
            }
        }

        int sum = 0;
        rep(i,v.size()){
            sum+=taiseki(v[i].x, v[i].y, v[i].z);
        }
        int res = min_cost_flow(S,T,n);
        cout<<sum + res<<endl;
    }
}

Measured memory_bytes: 3564