Problem:
マトリョーシカ
マトリョーシカはロシアの民芸品として有名な人形である．
マトリョーシカは上下に分割でき，開くと中により小さい別の人形が入っている．
現れた小さい人形を開くとさらに小さい人形が入っている，というような入れ子構造になっている．
あなたは旅行先で珍しい形のマトリョーシカを見つけ，
N
体の人形を購入した．
i
番目の人形の形状は，
x
i
× y
i
× z
i
の直方体である．
ひとしきりマトリョーシカを鑑賞したあなたは，マトリョーシカを仕舞おうとしている．
その前に，いくつかの人形を別の人形に格納することによって必要なスペースを減らしたい．
人形を格納する際には，まだ中にひとつも人形を格納していない人形にだけ，他の人形をひとつ格納できる．
ただし，直接的に格納される人形についてだけ数えるものとし，中に人形が入っている人形を別の人形に格納することはできる．
収納された人形は，外部から見えない状態になる．
ただし，以下の条件を満たさなければならない．
人形は回転してよいが，直方体のそれぞれの辺は，他方の直方体のいずれかの辺に平行
回転後，対応する辺同士の長さそれぞれについて，収納される側の人形の長さの方が短い
1 個の人形の中に直接収納できる人形の数は高々 1 個
押入れの容積は限られているので，外部から見えている人形の体積の和を最小化したい．
あなたの仕事は，人形を収納する操作を任意の回数繰り返して達成できる，外部から見えている人形の体積の和の最小値を求めるプログラムを作成することである．
Input
入力は複数のデータセットからなる．
データセットの個数は最大でも 50 個を超えない．
各データセットは次の形式で表される．
N
x
1
y
1
z
1
:
:
x
N
y
N
z
N
各データセットは
N + 1
行からなり，データセットの 1 行目には，人形の数を表す整数
N
が与えられる．
続く
N
行の内
i
行目には，
i
番目の人形の大きさを表す 3 つの整数
x
i
, y
i
, z
i
が半角スペース区切りで与えられる．
これらの整数は，
1 ≤ N, x
i
, y
i
, z
i
≤ 100
を満たす．
入力の終わりは 1 つのゼロからなる行で表される．
Output
各データセットについて，外部から見えている人形の体積の和の最小値を 1 行で出力せよ．
Sample Input
2
1 2 3
4 2 3
3
2 5 2
3 3 4
5 5 5
5
1 1 1
2 2 2
3 3 3
4 4 4
5 5 5
5
1 1 1
2 1 1
3 1 1
4 1 1
5 1 1
10
3 1 4
1 5 9
2 6 5
3 5 8
9 7 9
3 2 3
8 4 6
2 6 4
3 3 8
3 2 7
0
Output for Sample Input
24
145
125
15
864

Submitted solutions:

code_snippet: #include "bits/stdc++.h"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int INF = 1e9;
const ll LINF = 1e18;
template<class S,class T> ostream& operator << (ostream& out,const pair<S,T>& o){ out << "(" << o.first << "," << o.second << ")"; return out; }
template<class T> ostream& operator << (ostream& out,const vector<T> V){ for(int i = 0; i < V.size(); i++){ out << V[i]; if(i!=V.size()-1) out << " ";} return out; }
template<class T> ostream& operator << (ostream& out,const vector<vector<T> > Mat){ for(int i = 0; i < Mat.size(); i++) { if(i != 0) out << endl; out << Mat[i];} return out; }
template<class S,class T> ostream& operator << (ostream& out,const map<S,T> mp){ out << "{ "; for(auto it = mp.begin(); it != mp.end(); it++){ out << it->first << ":" << it->second; if(mp.size()-1 != distance(mp.begin(),it)) out << ", "; } out << " }"; return out; }

/*
 <url:>
 問題文============================================================
 =================================================================
 解説=============================================================
 ================================================================
 */

typedef ll PD_Type;
const PD_Type PD_INF = INF;
struct Primal_Dual
{
    typedef pair< PD_Type, int > pii;

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

    };
    vector< vector< edge > > graph;
    vector< int > prevv, preve;
    vector< PD_Type > potential, min_cost;
    Primal_Dual(int V) : graph(V) {}

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

    PD_Type min_cost_flow(int s, int t, int f)
    {
        int V = (int)graph.size();
        PD_Type ret = 0;
        priority_queue< pii, vector< pii >, greater< pii > > que;
        potential.assign(V, 0);
        preve.assign(V, -1);
        prevv.assign(V, -1);

        while (f > 0) {
            min_cost.assign(V, PD_INF);
            que.push(pii(0, s));
            min_cost[s] = 0;

            while (!que.empty()) {
                pii p = que.top();
                que.pop();
                if (min_cost[p.second] < p.first) continue;
                for (int i = 0; i < (int)graph[p.second].size(); i++) {
                    edge &e = graph[p.second][i];
                    PD_Type nextCost = min_cost[p.second] + e.cost + potential[p.second] - potential[e.to];
                    if (e.cap > 0 && min_cost[e.to] > nextCost) {
                        min_cost[e.to] = nextCost;
                        prevv[e.to] = p.second; preve[e.to] = i;
                        que.push(pii(min_cost[e.to], e.to));
                    }
                }
            }
            if (min_cost[t] == PD_INF) return -1;
            for (int v = 0; v < V; v++) potential[v] += min_cost[v];
            PD_Type addflow = f;
            for (int v = t; v != s; v = prevv[v]) {
                addflow = min(addflow, graph[prevv[v]][preve[v]].cap);
            }
            f -= addflow;
            ret += addflow * potential[t];
            for (int v = t; v != s; v = prevv[v]) {
                edge &e = graph[prevv[v]][preve[v]];
                e.cap -= addflow;
                graph[v][e.rev].cap += addflow;
            }
        }
        return ret;
    }
};

int N;
struct hako{
    int h,w,d;
    int V;
    hako(int h = 0,int w = 0,int d = 0):h(h),w(w),d(d){
        V = h * w * d;
    }
    bool operator < (const hako& hako2) const{
        if(h < hako2.h && w < hako2.w && d < hako2.d) return true;
        if(h < hako2.h && w < hako2.d && d < hako2.w) return true;
        if(h < hako2.w && w < hako2.h && d < hako2.d) return true;
        if(h < hako2.w && w < hako2.d && d < hako2.h) return true;
        if(h < hako2.d && w < hako2.h && d < hako2.w) return true;
        if(h < hako2.d && w < hako2.w && d < hako2.h) return true;
        return false;
    }
};
ll solve(){
    ll res = 0;
    Primal_Dual PD(2*N+2);
    int S = 2*N,T = S+1;
    vector<hako> hakos(N);
    for(int i = 0; i < N;i++){
        int x,y,z; cin >> x >> y >> z;
        hakos[i] = hako(x,y,z);
        //res += hakos[i].V;
        PD.add_edge(S, i, 1, 0);
        PD.add_edge(i,T,1,hakos[i].V);
        PD.add_edge(i+N, T, 1, 0);
    }
    for(int i = 0; i < N;i++){
        for(int j = 0; j < N;j++){
            if(i == j) continue;
            if(hakos[i] < hakos[j]){
                PD.add_edge(i, j+N, 1, 0);
            }
        }
    }
    res = PD.min_cost_flow(S, T, N);
//    cout << PD.min_cost_flow(S,T,N) << endl;
    return res;
}
int main(void) {
    cin.tie(0); ios_base::sync_with_stdio(false);
    while(cin >> N,N){
        cout << solve() << endl;
    }
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3756
final_rank: 9

code_snippet: #include <bits/stdc++.h>
#include <unordered_map>
#include <stdlib.h>
using namespace std;
#define rep(i, a, n) for(ll i = a; i < n; i++)
#define rrep(i, a, n) for(ll i = a; i >= n; i--)
#define ll long long
#define pii pair<int, int>
#define pll pair<ll, ll>
#define all(x) (x).begin(), (x).end()
//constexpr ll MOD = 1000000007;
constexpr ll MOD = 998244353;
constexpr int IINF = 1001001001;
constexpr ll INF = 1LL<<60;
template<class t,class u> void chmax(t&a,u b){if(a<b)a=b;}
template<class t,class u> void chmin(t&a,u b){if(b<a)a=b;}


ll gcd(ll a, ll b){
    if(a%b == 0){
      return b;
    }else{
      return gcd(b, a%b);
    }
}

ll lcm(ll a, ll b){
    return a*b / gcd(a, b);
}

ll powMod(ll x, ll n) {
    if (n == 0) return 1 % MOD;
    ll val = powMod(x, n / 2);
    val *= val;
    val %= MOD;
    if (n % 2 == 1) val *= x;
    return val % MOD;
}

struct Edge {
    ll to, cap, cost, rev;
};

struct MinCostFlow {
private:
    ll n; // 頂点数
    vector<vector<Edge>> graph; // 残余グラフの隣接表現リスト
    vector<ll> h; // ポテンシャル
    vector<ll> dist; // 最短距離
    vector<ll> prevv, preve; // 直前の頂点と辺

public:
    // コンストラクタ
    MinCostFlow(ll n_): n(n_) {
        graph.resize(n);
        h.resize(n);
        dist.resize(n);
        prevv.resize(n);
        preve.resize(n);
    }

    // 辺の追加
    void add_edge(ll from, ll to, ll cap, ll cost){
        ll size_from = (ll)graph[from].size(); // 現時点でのG[from]の要素数
        ll size_to = (ll)graph[to].size(); // 現時点でのG[to]の要素数
        graph[from].push_back((Edge){to, cap, cost, size_to});
        graph[to].push_back((Edge){from, 0, -cost, size_from});
    }

    // sからtへの流量fの最小費用流を求める
    // 流せない場合は-1を返す
    ll min_cost_flow(ll s, ll t, ll f) {
        ll res = 0;
        fill(h.begin(), h.end(), 0); // ポテンシャルを初期化

        while(f > 0) {
            // ダイクストラ法を用いてhを更新する
            priority_queue<pii, vector<pii>, greater<pii>> que;
            fill(dist.begin(), dist.end(), INF);
            dist[s] = 0;
            que.push({0, s});

            while(!que.empty()){
                pii q = que.top(); que.pop();
                ll v = q.second;
                if(dist[v] < q.first) continue;

                rep(i, 0, (ll)graph[v].size()){
                    Edge e = graph[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({dist[e.to], e.to});
                    }
                }
            }
            if(dist[t] == INF) {
                // これ以上流せない
                return -1;
            }
            // ポテンシャルの更新
            rep(i, 0, n){
                // ポテンシャルを除いた最短路に更新する
                h[i] = dist[i]+h[i]-h[s];
            }
            // s-t間最短路に沿って目一杯流す
            ll d = f;
            for(ll v = t; v != s; v = prevv[v]){
                d = min(d, graph[prevv[v]][preve[v]].cap);
            }
            f -= d;
            res += d*h[t];
            for(ll v = t; v != s; v = prevv[v]){
                Edge &e = graph[prevv[v]][preve[v]];
                e.cap -= d;
                graph[v][e.rev].cap += d;
            }
        }
        return res;
    }
};

int main() {
    while(true){
        ll n; cin >> n;
        if(n == 0) break;
        vector<ll> x(n), y(n), z(n);
        ll sum = 0;
        rep(i,0,n){
            cin >> x[i] >> y[i] >> z[i];
            if(y[i] > z[i]) swap(y[i],z[i]);
            if(x[i] > y[i]) swap(x[i],y[i]);
            if(y[i] > z[i]) swap(y[i],z[i]);
            sum += x[i]*y[i]*z[i];
        }
        MinCostFlow g(n*2+2);
        ll start = n*2, end = n*2+1;
        ll num = 1000000;
        rep(i,0,n){
            g.add_edge(start,i,1,0);
            g.add_edge(i+n,end,1,num-x[i]*y[i]*z[i]);
        }
        rep(i,0,n)rep(j,0,n){
            if(i == j) continue;
            if(x[i] > x[j] && y[i] > y[j] && z[i] > z[j]){
                g.add_edge(i,j+n,1,0);
            }
        }
        ll cs = 0;
        ll ans = sum;
        rep(i,1,n+1){
            ll cost = g.min_cost_flow(start,end,1);
            if(cost == -1) break;
            cs -= num-cost;
            chmin(ans,sum+cs);
        }
        cout << ans << endl;
    }
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3736
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define ALL(x) (x).begin(),(x).end()
#define REP(i, n) for(ll i=0; i<(ll)(n); i++)
#define PER(i, n) for(ll i=(ll)(n)-1; i>=0; i--)

template<typename T> int LB(const vector<T>& v, T x) { return lower_bound(ALL(v),x)-v.begin(); }
template<typename T> int UQ(T& v) { sort(ALL(v)); v.erase(unique(ALL(v)),v.end()); return v.size(); }
template<typename T> bool chmax(T &a, T b) { return a<b ? a=b, true : false; }
template<typename T> bool chmin(T &a, T b) { return a>b ? a=b, true : false; }
template<typename T> using rpriority_queue = priority_queue<T,vector<T>,greater<T>>;
using ll=long long; const int INF=1e9+10; const ll INFL=4e18;
using ld=long double; using lll=__int128_t; using ull=unsigned long long;
using VI=vector<int>; using VVI=vector<VI>; using VL=vector<ll>; using VVL=vector<VL>;
using PL=pair<ll,ll>; using VP=vector<PL>; using WG=vector<vector<pair<int,ll>>>;


#ifdef LOCAL
#include "./debug.hpp"
#else
#define debug(...)
#define print_line
#endif



/// @brief 最小費用流
/// @note コスト負の辺を許容する。負の閉路はダメ。
struct MinCostFlow {
    /// @brief 辺構造体
    struct Edge {
        int from; ///< 始点
        int to; ///< 終点
        int rev; ///< 逆辺のインデックス
        ll cap; ///< 容量
        ll cost; ///< コスト
        bool isrev;
        Edge(int from, int to, ll cap, ll cost, int rev, bool isrev):from(from),to(to),cap(cap),cost(cost),rev(rev),isrev(isrev) {}
    };

    MinCostFlow(int n) { graph.resize(n),dist.resize(n),pot.resize(n),pv.resize(n),pe.resize(n); }

    /// @brief s -> t に容量 cap, コスト cost の辺を追加する
    /// @note cost は負でも良い
    void add_edge(int from, int to, ll cap, ll cost) {
        graph[from].push_back(Edge(from,to,cap,cost,graph[to].size(),false));
        graph[to].push_back(Edge(to,from,0,-cost,(int)graph[from].size()-1,true));
    }

    /// @brief 全ての辺を返す
    /// @note O(V+E)
    /// @note 直前に流した flow による残余であることに注意
    vector<Edge> get_edges() {
        vector<Edge> ret;
        for(int i=0; i<graph.size(); i++) for(auto &e:graph[i]) if(!e.isrev) ret.push_back(e);
        return ret;
    }

    /// @brief s から t へ流量 f の最小費用流のコストを求める
    /// @brief 流せない場合は INFL を返す
    /// @note O(FE log V)
    ll flow(int s, int t, ll f) {
        int n=graph.size();
        rpriority_queue<pair<ll,int>> pq;
        fill(pot.begin(),pot.end(),0);
        fill(pv.begin(),pv.end(),0);
        fill(pe.begin(),pe.end(),0);
        ll ret=0;

        while(f>0) {
            fill(dist.begin(),dist.end(),INFL);
            dist[s]=0;
            pq.push({0,s});
            while(!pq.empty()) {
                auto [tmp,now]=pq.top();
                pq.pop();
                if(dist[now]<tmp) continue;
                for(int i=0; i<graph[now].size(); i++) {
                    auto [from,to,rev,cap,cost,isrev]=graph[now][i];
                    ll ncost=dist[now]+cost+pot[now]-pot[to];
                    if(cap>0&&dist[to]>ncost) {
                        dist[to]=ncost,pv[to]=now,pe[to]=i;
                        pq.push({dist[to],to});
                    }
                }
            }
            if(dist[t]==INFL) return INFL;
            for(int i=0; i<n; i++) pot[i]+=dist[i];
            ll d=f;
            for(int v=t; v!=s; v=pv[v]) d=min(d,graph[pv[v]][pe[v]].cap);
            f-=d,ret+=d*pot[t];
            for(int v=t; v!=s; v=pv[v]) {
                auto &e=graph[pv[v]][pe[v]];
                e.cap-=d,graph[v][e.rev].cap+=d;
            }
        }

        return ret;
    }

private:
    vector<vector<Edge>> graph;
    vector<ll> dist,pot; // 距離, ポテンシャル
    vector<int> pv,pe;
};

//----------------------------------------------------------


bool solve() {
    ll N; cin>>N;
    if(N==0) return false;

    VVL XYZ(N,VL(3));
    REP(i,N) {
        REP(j,3) cin>>XYZ[i][j];
        sort(ALL(XYZ[i]));
    }

    MinCostFlow mcf(2*N+2);
    ll S=2*N, T=S+1;
    ll ans=0;
    REP(i,N) {
        mcf.add_edge(S,i,1,0);
        mcf.add_edge(i,N+i,1,0);
        mcf.add_edge(N+i,T,1,0);
        ans+=XYZ[i][0]*XYZ[i][1]*XYZ[i][2];
    }

    REP(i,N) {
        REP(j,N) {
            if(XYZ[i][0]<XYZ[j][0] && XYZ[i][1]<XYZ[j][1] && XYZ[i][2]<XYZ[j][2]) {
                mcf.add_edge(N+i,j,1,-XYZ[i][0]*XYZ[i][1]*XYZ[i][2]);
            }
        }
    }

    while(true) {
        ll res=mcf.flow(S,T,1);
        if(res==INFL || res>0) break;
        debug(res);
        ans+=res;
    }

    cout<<ans<<'\n';

    return true;
}


int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(15);
    cerr << fixed << setprecision(15);
    while (true) {
        if (!solve()) {
            break;
        }
    }
}
accuracy: 1
time_ms: 10
memory_kb: 3684
final_rank: 6

code_snippet: // (⁠◕⁠ᴗ⁠◕⁠✿⁠)

// #pragma GCC target("avx2")
// #pragma GCC optimize("O3")
// #pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
#define rep(i, n) for (ll i = 0; i < (n); i++)
#define srep(i, s, n) for (ll i = s; i < (n); i++)
#define len(x) ((int)(x).size())
#define all(x) (x).begin(), (x).end()
using namespace std;
template<typename T> using vc = vector<T>;
template<typename T> using vv = vc<vc<T>>;
template<typename T> using vvv = vv<vc<T>>;
using vi = vc<int>;using vvi = vv<int>; using vvvi = vv<vi>;
using ll = long long;using vl = vc<ll>;using vvl = vv<ll>; using vvvl = vv<vl>;
using ld = long double; using vld = vc<ld>; using vvld = vc<vld>; using vvvld = vc<vvld>;
using uint = unsigned int;
using ull = unsigned long long;
const ld pi = 3.141592653589793;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
// const ll mod = 1000000007;
const ll mod = 998244353;
inline bool inside(ll y, ll x, ll H, ll W) {return 0 <= (y) and (y) < (H) and 0 <= (x) and (x) < (W); }

#define debug(var)  do{std::cout << #var << " : \n";view(var);}while(0)
template<typename T> void view(T e){cout << e << endl;}
template<typename T> void view(const vc<T>& v){for(const auto& e : v){ cout << e << " "; } cout << endl;}
template<typename T> void view(const vv<T>& vv){ for(const auto& v : vv){ view(v); } }

// ll
struct mincost_flow{
    public : 
        mincost_flow(int n) : _n(n){
            g = vv<edge>(_n);
            dist = vl(_n, INF);
            prevv = vi(_n);
            preve = vi(_n);
        }

        void add_edge(int from, int to, ll cap, ll cost){
            g[from].push_back((edge){to, cap, cost, len(g[to])});
            g[to].push_back((edge){from, 0, -cost, len(g[from]) - 1});
        }

        ll flow(int s, int t, ll f){
            ll ret = 0;
            while (f > 0){
                rep(i, _n) dist[i] = INF;
                dist[s] = 0;
                bool update = true;
                while (update){
                    update = false;
                    rep(v, _n){
                        if (dist[v] == INF) continue;
                        int i = 0;
                        for (auto &e : g[v]){
                            if (e.cap > 0 && dist[e.to] > dist[v] + e.cost){
                                dist[e.to] = dist[v] + e.cost;
                                prevv[e.to] = v;
                                preve[e.to] = i;
                                update = true;
                            }
                            i++;
                        }
                    }
                }
                if (dist[t] == INF) return INF;
                ll d = f;
                for (int v = t; v != s; v = prevv[v]) d = min(d, g[prevv[v]][preve[v]].cap);
                f -= d;
                ret += d * dist[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 ret;
        }

    private : 
        struct edge {int to; ll cap, cost; int rev;};
        int _n;
        vv<edge> g;
        vl dist;
        vi prevv, preve;
};


void solve(int N){
    vvi X(N, vi(3));
    rep(i, N) rep(j, 3) cin >> X[i][j];
    rep(i, N) sort(all(X[i]));
    vvl A(N, vl(N, 0));
    vl w(N); rep(i, N) w[i] = X[i][0] * X[i][1] * X[i][2];
    rep(i, N) rep(j, N) if (X[j][0] < X[i][0] && X[j][1] < X[i][1] && X[j][2] < X[i][2]){
        A[i][j] = w[j];
    }
    mincost_flow G(2 * N + 2);
    int s = 2 * N, t = 2 * N + 1;
    rep(i, N) rep(j, N){
        G.add_edge(i, j + N, 1, -A[i][j]);
    }
    rep(i, N){
        G.add_edge(s, i, 1, 0);
        G.add_edge(i + N, t, 1, 0);
    }
    cout << accumulate(all(w), 0ll) + G.flow(s, t, N) << endl;
}

int main(){
    while (true){
        int N; cin >> N;
        if (N == 0) break;
        solve(N);
    }
}
accuracy: 1
time_ms: 50
memory_kb: 4376
final_rank: 17

code_snippet: #include<bits/stdc++.h>
using namespace std;

//* ATCODER
#include<atcoder/all>
using namespace atcoder;
typedef modint998244353 mint;
//*/

/* BOOST MULTIPRECISION
#include<boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision;
//*/

typedef long long ll;

#define rep(i, s, n) for (int i = (int)(s); i < (int)(n); i++)
#define rrep(i, s, n) for (int i = (int)(n)-1; i >= (int)(s); i--)
#define all(v) begin(v),end(v)

template <typename T> bool chmin(T &a, const T &b) {
	if (a <= b) return false;
	a = b;
	return true;
}

template <typename T> bool chmax(T &a, const T &b) {
	if (a >= b) return false;
	a = b;
	return true;
}

template <typename T> T max(vector<T> &a){
	assert(!a.empty());
	T ret = a[0];
	for (int i=0; i<(int)a.size(); i++) chmax(ret, a[i]);
	return ret;
}

template <typename T> T min(vector<T> &a){
	assert(!a.empty());
	T ret = a[0];
	for (int i=0; i<(int)a.size(); i++) chmin(ret, a[i]);
	return ret;
}

template <typename T> T sum(vector<T> &a){
	T ret = 0;
	for (int i=0; i<(int)a.size(); i++) ret += a[i];
	return ret;
}

// importbisect
template <typename T>
int bisect_left(vector<T> &X, T v){
	return lower_bound(X.begin(), X.end(), v) - X.begin();
}

template <typename T>
int bisect_right(vector<T> &X, T v){
	return upper_bound(X.begin(), X.end(), v) - X.begin();
}
// ----

// defcomp
template <typename T>
vector<T> compress(vector<T> &X) {
	vector<T> vals = X;
	sort(vals.begin(), vals.end());
	vals.erase(unique(vals.begin(), vals.end()), vals.end());
	return vals;
}
// -----

//defmodfact
const int COMinitMAX = 1100000;
mint fact[COMinitMAX+1], factinv[COMinitMAX+1];

void modfact(){
	fact[0] = 1;
	for (int i=1; i<=COMinitMAX; i++){
		fact[i] = fact[i-1] * i;
	}
	factinv[COMinitMAX] = fact[COMinitMAX].inv();
	for (int i=COMinitMAX-1; i>=0; i--){
		factinv[i] = factinv[i+1] * (i+1);
	}
}

mint cmb(int a, int b){
	if (a<b || b<0) return mint(0);
	return fact[a]*factinv[b]*factinv[a-b];
}
//--------

// makediv
vector<ll> makediv(ll n){
	vector<ll> ld, ud;
	for (ll i=1; i*i<=n; i++){
		if (n%i == 0){
			ld.push_back(i);
			if (i != n/i){
				ud.push_back(n/i);
			}
		}
	}
	reverse(ud.begin(), ud.end());
	ld.insert(ld.end(), ud.begin(), ud.end());
	return ld;
}
// -----

// Fast Factorization
// https://judge.yosupo.jp/submission/38126
// !!! CHANGED THE PRIMARY TEST !!!
typedef unsigned int uint;

struct Mint {
	uint64_t n;
	static uint64_t mod, inv, r2;
	Mint() : n(0) { }
	Mint(const uint64_t &x) : n(init(x)) { }
	static uint64_t init(const uint64_t &w) { return reduce(__uint128_t(w) * r2); }
	static void set_mod(const uint64_t &m) {
		mod = inv = m;
		for(int i = 0; i < 5; i++)	inv *= 2 - inv * m;
		r2 = -__uint128_t(m) % m;
	}
	static uint64_t reduce(const __uint128_t &x) {
		uint64_t y = uint64_t(x >> 64) - uint64_t((__uint128_t(uint64_t(x) * inv) * mod) >> 64);
		return int64_t(y) < 0 ? y + mod : y;
	}
	Mint& operator+= (const Mint &x) { n += x.n - mod; if(int64_t(n) < 0) n += mod; return *this; }
	Mint& operator+ (const Mint &x) const { return Mint(*this) += x; }
	Mint& operator*= (const Mint &x) { n = reduce(__uint128_t(n) * x.n); return *this; }
	Mint& operator* (const Mint &x) const { return Mint(*this) *= x; }
	uint64_t val() const { return reduce(n); }
};

uint64_t Mint::mod, Mint::inv, Mint::r2;

bool suspect(const uint64_t &a, const uint64_t &s, uint64_t d, const uint64_t &n) {
	if(Mint::mod != n)	Mint::set_mod(n);
	Mint x(1), xx(a), o(x), m(n - 1);
	while(d > 0) {
		if(d & 1)	x *= xx;
		xx *= xx;
		d >>= 1;
	}
	if(x.n == o.n)	return true;
	for(uint r = 0; r < s; r++) {
		if(x.n == m.n)	return true;
		x *= x;
	}
	return false;
}

bool is_prime(const uint64_t &n) {
	if(n <= 1 || (n > 2 && (~n & 1)))	return false;
	uint64_t d = n - 1, s = 0;
	while(~d & 1)	s++, d >>= 1;
	static const uint64_t a_big[] = {2, 325, 9375, 28178, 450775, 9780504, 1795265022};
	static const uint64_t a_smo[] = {2, 7, 61};
	if(n < 4759123141LL) {
		for(auto &&p : a_smo) {
			if(p >= n)	break;
			if(!suspect(p, s, d, n))	return false;
		}
	} else {
		for(auto &&p : a_big) {
			if(p >= n)	break;
			if(!suspect(p, s, d, n))	return false;
		}
	}
	return true;
}

uint64_t rho_pollard(const uint64_t &n) {
	if(~n & 1)	return 2u;
	static random_device rng;
	uniform_int_distribution<uint64_t> rr(1, n - 1);
	if(Mint::mod != n)	Mint::set_mod(n);
	for(;;) {
		uint64_t c_ = rr(rng), g = 1, r = 1, m = 500;
		Mint y(rr(rng)), xx(0), c(c_), ys(0), q(1);
		while(g == 1) {
			xx.n = y.n;
			for(uint i = 1; i <= r; i++)	y *= y, y += c;
			uint64_t k = 0; g = 1;
			while(k < r && g == 1) {
				for(uint i = 1; i <= (m > (r - k) ? (r - k) : m); i++) {
					ys.n = y.n;
					y *= y; y += c;
					uint64_t xxx = xx.val(), yyy = y.val();
					q *= Mint(xxx > yyy ? xxx - yyy : yyy - xxx);
				}
				g = __gcd<uint64_t>(q.val(), n);
				k += m;
			}
			r *= 2;
		}
		if(g == n)	g = 1;
		while(g == 1) {
			ys *= ys; ys += c;
			uint64_t xxx = xx.val(), yyy = ys.val();
			g = __gcd<uint64_t>(xxx > yyy ? xxx - yyy : yyy - xxx, n);
		}
		if(g != n && is_prime(g))	return g;
	}
	assert(69 == 420);
}

template <typename T>
vector<T> inter_factor(const T &n) {
	if(n < 2)	return { };
	if(is_prime(n))	return {n};
	T d = rho_pollard(static_cast<uint64_t>(n));
	vector<T> l = inter_factor(d), r = inter_factor(n / d);
	l.insert(l.end(), r.begin(), r.end());
	return l;
}

template <typename T>
vector<T> factor(T n) {
	vector<T> f1;
	for(uint i = 2; i < 100; i += (i & 1) + 1)
		while(n % i == 0)	f1.push_back(i), n /= i;
	vector<T> f2 = inter_factor(n);
	f1.insert(f1.end(), f2.begin(), f2.end());
	sort(f1.begin(), f1.end());
	return f1;
}


template<class Cap, class Cost> struct shobon_mcf {
   struct E {
      int from;
      int to;
      int rev;
      Cap cap;
      Cost cost;
   };

   int n;
   vector<E> e;
   vector<Cost> p;
   vector<vector<int>> ikeru;
   const Cost inf = numeric_limits<Cost>::max() / 4;

   shobon_mcf(int n) : n(n) {
      ikeru.resize(n);
      p.resize(n);
   }

   void add_edge(int from, int to, Cap cap, Cost cost) {
      int x = e.size();
      e.push_back({from, to, x + 1, cap, cost});
      e.push_back({to, from, x, 0, -cost});
      ikeru[from].push_back(x);
      ikeru[to].push_back(x + 1);
   }

   pair<Cap, Cost> flow(int from, int to, Cap f) {
      Cap cap = 0;
      Cost cost = 0;

      while(f > 0) {
         vector<Cost> d(n, inf);
         vector<int> cf(n, -1);
         priority_queue<pair<Cost, int>> pq;
         pq.push(pair(0, from));
         d[from] = 0;
         while(!pq.empty()) {
            auto [tmp, i] = pq.top();
            pq.pop();
            tmp = -tmp;
            if(tmp > d[i]) continue;
            for(int x : ikeru[i]) {
               int j = e[x].to;
               if(e[x].cap == 0) continue;
               if(chmin(d[j], e[x].cost + tmp + p[i] - p[j])) {
                  cf[j] = x;
                  pq.push(pair(-d[j], j));
               }
            }
         }

         if(d[to] == inf) break;
         rep(i, 0, n) p[i] += d[i];

         Cap z = f;
         int nw = to;
         while(nw != from) {
            chmin(z, e[cf[nw]].cap);
            nw = e[cf[nw]].from;
         }

         f -= z;
         cap += z;
         cost += z * p[to];

         nw = to;
         while(nw != from) {
            e[cf[nw]].cap -= z;
            e[e[cf[nw]].rev].cap += z;
            nw = e[cf[nw]].from;
         }
      }

      return pair(cap, cost);
   }

   pair<Cap, Cost> flow(int from, int to) { return flow(from, to, numeric_limits<Cap>::max()); }

   // Type the following code if you need
   // Return the edges

   vector<E> edges() {
      vector<E> ret((int)e.size() / 2);
      rep(i, 0, (int)e.size() / 2) ret[i] = e[2 * i];
      return ret;
   }

   // Init the potential with Bellman-Ford

   void potential_bellman_ford(int from) {
      int m = (int)e.size() / 2;
      fill(all(p), inf);
      p[from] = 0;
      rep(num, 0, n) {
         rep(x, 0, m) {
            int i = e[2 * x].from;
            int j = e[2 * x].to;
            chmin(p[j], p[i] + e[2 * x].cost);
         }
      }
   }

   // Init the potential with DP on DAG

   void potential_dag(int from) {
      vector<int> deg(n);
      rep(x, 0, (int)e.size()) {
         if(e[x].cap > 0) { deg[e[x].to]++; }
      }

      assert(deg[from] == 0);

      fill(all(p), inf);
      p[from] = 0;
      vector<int> mada;
      rep(i, 0, n) {
         if(deg[i] == 0) mada.push_back(i);
      }

      int cnt = 0;
      while(!mada.empty()) {
         int i = mada.back();
         mada.pop_back();
         cnt++;
         for(int x : ikeru[i]) {
            if(e[x].cap == 0) continue;
            int j = e[x].to;
            chmin(p[j], p[i] + e[x].cost);
            deg[j]--;
            if(deg[j] == 0) { mada.push_back(j); }
         }
      }

      assert(cnt == n);
   }
};

void solve(int n){
	vector<int> x(n),y(n),z(n);
	rep(i,0,n){
		cin>>x[i]>>y[i]>>z[i];
		if(x[i]>y[i])swap(x[i],y[i]);
		if(y[i]>z[i])swap(y[i],z[i]);
		if(x[i]>y[i])swap(x[i],y[i]);
		assert(x[i]<=y[i]&&y[i]<=z[i]);
	}
	
	shobon_mcf<int,ll> mcf(2*n+2);
	rep(i,0,n){
		rep(j,0,n){
			if(i==j)continue;
			if(x[i]<x[j]&&y[i]<y[j]&&z[i]<z[j]){
				mcf.add_edge(i,j+n,1,0LL);
			}
		}
	}
	
	rep(i,0,n){
		mcf.add_edge(2*n,i,1,-(ll)x[i]*y[i]*z[i]);
		mcf.add_edge(i+n,2*n+1,1,0);
	}

	mcf.potential_dag(2*n);
	ll geta=0;
	rep(i,0,n){
		geta+=(ll)x[i]*y[i]*z[i];
	}
	cout<<geta+mcf.flow(2*n,2*n+1).second<<'\n';

}

int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	while(true){
		int n;cin>>n;
		if(n==0)break;
		solve(n);
	}
}
accuracy: 1
time_ms: 10
memory_kb: 12604
final_rank: 19

code_snippet: #include <algorithm>
#include <array>
#include <atomic>
#include <bitset>
#include <cassert>
#include <cctype>
#include <cerrno>
#include <cfenv>
#include <cfloat>
#include <chrono>
#include <climits>
#include <clocale>
#include <cmath>
#include <complex>
#include <condition_variable>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <exception>
#include <filesystem>
#include <forward_list>
#include <fstream>
#include <functional>
#include <future>
#include <initializer_list>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <mutex>
#include <new>
#include <numeric>
#include <optional>
#include <ostream>
#include <queue>
#include <random>
#include <ratio>
#include <regex>
#include <scoped_allocator>
#include <set>
#include <shared_mutex>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <string_view>
#include <system_error>
#include <thread>
#include <tuple>
#include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <valarray>
#include <variant>
#include <vector>
using namespace std;

#define rep2(i, m, n) for (int i = (m); i < (n); ++i)
#define rep(i, n) rep2(i, 0, n)
#define drep2(i, m, n) for (int i = (m)-1; i >= (n); --i)
#define drep(i, n) drep2(i, n, 0)
#define all(...) std::begin(__VA_ARGS__), std::end(__VA_ARGS__)
#define INF (long long)1001001001001001001
#define inf 1001001000
#define fi first
#define se second

using ll = long long;
using vi = vector<int>;
using vvi  = vector<vector<int>>;
using vl = vector<long long>;
using pii = pair<int, int>;
using pll = pair<long long, long long>;
using LL = __int128_t;

ll gcd(ll x, ll y) {if (x == 0) return y;	return gcd(y%x, x);} 
ll lcm(ll x, ll y) { __int128_t xx,yy; xx=x; yy=y; __int128_t ans=xx * yy / gcd(x, y); ll ans2=ans; return ans2; }
template<typename T>
T POW(T x, ll n){T ret=1;	while(n>0){		if(n&1) ret=ret*x;		x=x*x;		n>>=1;	}	return ret;}
template<typename T>
T modpow(T a, ll n, T p) {	if(n==0) return (T)1;  if (n == 1) return a % p;  if (n % 2 == 1) return (a * modpow(a, n - 1, p)) % p;  T t = modpow(a, n / 2, p);  return (t * t) % p;}
template<typename T>
T modinv(T a, T m) {	if(m==0)return (T)1;	T b = m, u = 1, v = 0;	while (b) {		T t = a / b;		a -= t * b; swap(a, b);		u -= t * v; swap(u, v);	}	u %= m;	if (u < 0) u += m;	return u;}
ll REM(ll a, ll b){ return (a % b + b) % b;}
ll QUO(ll a, ll b){ return (a - REM(a, b)) / b;}
/*
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dist1(1, 100); [1,100]
int random_value = dist1(gen);
*/
/*
auto start = chrono::steady_clock::now(); //時間計測の開始
auto now = std::chrono::steady_clock::now(); //現在時刻と開始時刻の差を測定
double elapsed = std::chrono::duration<double>(now - start).count(); //時間をdouble型で取得
*/
/*
const int MAXCOMB=510000;
std::vector<mint> FAC(MAXCOMB), FINV(MAXCOMB), INV(MAXCOMB);
void COMinit() {FAC[0] = FAC[1] = 1;FINV[0] = FINV[1] = 1;INV[1] = 1;for (int i = 2; i < MAXCOMB; i++) {FAC[i] = FAC[i - 1] * i;INV[i] = mint(0) - INV[mint::mod() % i] * (mint::mod() / i);FINV[i] = FINV[i - 1] * INV[i];}}
mint COM(int n, int k) {if (n < k) return 0;if (n < 0 || k < 0) return 0;return FAC[n] * FINV[k] * FINV[n - k];}
*/

template <typename T> inline bool chmax(T &a, T b) { return ((a < b) ? (a = b, true) : (false));}
template <typename T> inline bool chmin(T &a, T b) { return ((a > b) ? (a = b, true) : (false));}



template <class Cap, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    mcf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap, cost});
        g[to].push_back(_edge{from, from_id, 0, -cost});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{
            pos[i].first, _e.to, _e.cap + _re.cap, _re.cap, _e.cost,
        };
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result(m);
        for (int i = 0; i < m; i++) {
            result[i] = get_edge(i);
        }
        return result;
    }

    std::pair<Cap, Cost> flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
        return slope(s, t, flow_limit).back();
    }
    std::vector<std::pair<Cap, Cost> > slope(int s, int t) {
        return slope(s, t, std::numeric_limits<Cap>::max());
    }
    std::vector<std::pair<Cap, Cost> > slope(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);
        // variants (C = maxcost):
        // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
        // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge
        std::vector<Cost> dual(_n, 0), dist(_n);
        std::vector<int> pv(_n), pe(_n);
        std::vector<bool> vis(_n);
        auto dual_ref = [&]() {
            std::fill(dist.begin(), dist.end(),
                      std::numeric_limits<Cost>::max());
            std::fill(pv.begin(), pv.end(), -1);
            std::fill(pe.begin(), pe.end(), -1);
            std::fill(vis.begin(), vis.end(), false);
            struct Q {
                Cost key;
                int to;
                bool operator<(Q r) const { return key > r.key; }
            };
            std::priority_queue<Q> que;
            dist[s] = 0;
            que.push(Q{0, s});
            while (!que.empty()) {
                int v = que.top().to;
                que.pop();
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                // dist[v] = shortest(s, v) + dual[s] - dual[v]
                // dist[v] >= 0 (all reduced cost are positive)
                // dist[v] <= (n-1)C
                for (int i = 0; i < int(g[v].size()); i++) {
                    auto e = g[v][i];
                    if (vis[e.to] || !e.cap) continue;
                    // |-dual[e.to] + dual[v]| <= (n-1)C
                    // cost <= C - -(n-1)C + 0 = nC
                    Cost cost = e.cost - dual[e.to] + dual[v];
                    if (dist[e.to] - dist[v] > cost) {
                        dist[e.to] = dist[v] + cost;
                        pv[e.to] = v;
                        pe[e.to] = i;
                        que.push(Q{dist[e.to], e.to});
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                // dual[v] = dual[v] - dist[t] + dist[v]
                //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) + (shortest(s, v) + dual[s] - dual[v])
                //         = - shortest(s, t) + dual[t] + shortest(s, v)
                //         = shortest(s, v) - shortest(s, t) >= 0 - (n-1)C
                dual[v] -= dist[t] - dist[v];
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost> > result;
        result.push_back({flow, cost});
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = pv[v]) {
                c = std::min(c, g[pv[v]][pe[v]].cap);
            }
            for (int v = t; v != s; v = pv[v]) {
                auto& e = g[pv[v]][pe[v]];
                e.cap -= c;
                g[v][e.rev].cap += c;
            }
            Cost d = -dual[s];
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }

  private:
    int _n;

    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<int, int> > pos;
    std::vector<std::vector<_edge> > g;
};
/////////////////////////////////////////////////////////////////////////////////////////
int SOLVEFIN = 0;

void solve() {
  int n;
  cin >> n;
  if (n == 0) {
    SOLVEFIN=1;
    return;
  }
  vector<ll>x(n),y(n),z(n);
  rep(i, n) { cin >> x[i] >> y[i] >> z[i]; }

  ll ans = 1e15;
  ll cost_sum = 0;
  rep(i, n) {
    cost_sum+=x[i]*y[i]*z[i];
  }
  for (int k = 0; k <= n; k++) {
    mcf_graph<ll, ll> graph(n * 2 + 2);
    ll M=1e9;
    rep(i,n){
        rep(j, n) {
        int flg = 0;
        if (x[i] > x[j] && y[i] > y[j] && z[i] > z[j]) flg=1;
        if (x[i] > x[j] && y[i] > z[j] && z[i] > y[j]) flg=1;
        if (x[i] > y[j] && y[i] > x[j] && z[i] > z[j]) flg=1;
        if (x[i] > y[j] && y[i] > z[j] && z[i] > x[j]) flg=1;
        if (x[i] > z[j] && y[i] > x[j] && z[i] > y[j]) flg=1;
        if (x[i] > z[j] && y[i] > y[j] && z[i] > x[j]) flg = 1;
        if (flg) {
            graph.add_edge(i, n+j, 1, 0);
            }
        }
    }
    rep(i, n) {
        graph.add_edge(n * 2, i, 1, 0);
        graph.add_edge(n+i, n*2+1, 1, M-(x[i]*y[i]*z[i]));
    }
    auto res = graph.flow(n * 2, n * 2 + 1,k);
    ans = min(ans, cost_sum-(M * res.first - res.second));
    //cout << res.first << " " << res.second << endl;
    if(res.first<k)break;
  }
  cout<<ans<<endl;


}

signed main(){
	cin.tie(0);
	ios::sync_with_stdio(0);
	cout<<fixed<<setprecision(20);
	while(SOLVEFIN == 0) solve();
}
accuracy: 1
time_ms: 380
memory_kb: 3676
final_rank: 20

code_snippet: #include<bits/stdc++.h>
using namespace std;

using ll =long long;

#define rep(i,n) for(ll i=0;i<n;i++)

void chmin(ll &a,ll b){
	a=min(a,b);
}

template<class ft = ll, class ct = ll>
struct PrimalDual {
  const ct INF = 1e18;
  struct edge {
    ll to, rev;
    ft cap;
    ct cost;
  };

  ll n, m;
  vector<vector<edge>> g;
  vector<array<ll, 2>> pos;
  vector<ct> pot, minc; // potential, min_cost
  vector<ll> pvs, pids; // prev_v, prev_id

  PrimalDual() = default;
  PrimalDual(ll n) : n(n), m(0), g(n) {}

  ll add_edge(ll from, ll to, ft cap, ct cost) {
    ll tid = g[to].size(), fid = g[from].size();
    g[from].push_back(edge{to, tid, cap, cost});
    g[to].push_back(edge{from, fid, 0, -cost});
    pos.push_back({from, fid});
    return m++;
  }

  pair<ft, ct> min_cost_flow(ll s, ll t, ft limit = 1e18) {
    ft f = 0;
    ct c = 0;
    using P = pair<ct, ll>;
    priority_queue<P, vector<P>, greater<P>> pq;
    pot.assign(n, 0);
    pvs.assign(n, -1);
    pids.assign(n, -1);

    while(limit > 0) {
      minc.assign(n, INF);
      minc[s] = 0;
      pq.emplace(minc[s], s);
      while(pq.size()) {
        auto [c, v] = pq.top();
        pq.pop();
        if(minc[v] < c) continue;
        rep(i, (ll)g[v].size()) {
          auto &e = g[v][i];
          ct nc = minc[v] + e.cost + pot[v] - pot[e.to];
          if(e.cap > 0 && minc[e.to] > nc) {
            minc[e.to] = nc;
            pvs[e.to] = v;
            pids[e.to] = i;
            pq.emplace(minc[e.to], e.to);
          }
        }
      }

      if(minc[t] == INF) break;
      rep(v, n) pot[v] += minc[v];
      ft addf = limit;
      for(ll v = t; v != s; v = pvs[v]) { chmin(addf, g[pvs[v]][pids[v]].cap); }
      limit -= addf;
      f += addf;
      c += addf * pot[t];
      for(ll v = t; v != s; v = pvs[v]) {
        auto &e = g[pvs[v]][pids[v]];
        e.cap -= addf;
        g[e.to][e.rev].cap += addf;
      }
    }
    return make_pair(f, c);
  }

};

void solve(ll N){
	vector<array<ll,3>> P(N);
	vector<ll> V(N);
	ll an=0;
	PrimalDual D(2*N+2);
	D.add_edge(2*N,2*N+1,N,0);
	for(int i=0;i<N;i++){
		cin>>P[i][0]>>P[i][1]>>P[i][2];
		sort(P[i].begin(),P[i].end());
		D.add_edge(2*N,i,1,0);
		D.add_edge(i+N,2*N+1,1,0);
		V[i]=P[i][0]*P[i][1]*P[i][2];
		an+=V[i];
	}
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++){
			bool OK=1;
			for(int t=0;t<3;t++){
				if(P[i][t]>=P[j][t])OK=0;
			}
			if(OK){
				D.add_edge(i,j+N,1,-V[i]);
			}
		}
	}
	cout<<an+D.min_cost_flow(2*N,2*N+1).second<<endl;;;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

	ll N;
	while(cin>>N,N!=0)solve(N);
}
accuracy: 1
time_ms: 10
memory_kb: 3968
final_rank: 13

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

#include <unistd.h>

namespace fastio {
static constexpr uint32_t SZ = 1 << 17;
char ibuf[SZ];
char obuf[SZ];
char out[100];
// pointer of ibuf, obuf


uint32_t pil = 0, pir = 0, por = 0;

struct Pre {
    char num[10000][4];
    constexpr Pre() : num() {
        for (int i = 0; i < 10000; i++) {
            int n = i;
            for (int j = 3; j >= 0; j--) {
                num[i][j] = n % 10 | '0';
                n /= 10;
            }
        }
    }
} constexpr pre;

inline void load() {
    memmove(ibuf, ibuf + pil, pir - pil);
    pir = pir - pil + fread(ibuf + pir - pil, 1, SZ - pir + pil, stdin);
    pil = 0;
    if (pir < SZ)
        ibuf[pir++] = '\n';
}

inline void flush() {
    fwrite(obuf, 1, por, stdout);
    por = 0;
}

void rd(char &c) {
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
}

void rd(string &x) {
    x.clear();
    char c;
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
    do {
        x += c;
        if (pil == pir)
            load();
        c = ibuf[pil++];
    } while (!isspace(c));
}

template <typename T> void rd_real(T &x) {
    string s;
    rd(s);
    x = stod(s);
}

template <typename T> void rd_integer(T &x) {
    if (pil + 100 > pir)
        load();
    char c;
    do
        c = ibuf[pil++];
    while (c < '-');
    bool minus = 0;
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (c == '-') {
            minus = 1, c = ibuf[pil++];
        }
    }
    x = 0;
    while ('0' <= c) {
        x = x * 10 + (c & 15), c = ibuf[pil++];
    }
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (minus)
            x = -x;
    }
}

void rd(int &x) {
    rd_integer(x);
}
void rd(ll &x) {
    rd_integer(x);
}
void rd(i128 &x) {
    rd_integer(x);
}
void rd(uint &x) {
    rd_integer(x);
}
void rd(ull &x) {
    rd_integer(x);
}
void rd(u128 &x) {
    rd_integer(x);
}
void rd(double &x) {
    rd_real(x);
}
void rd(long double &x) {
    rd_real(x);
}

template <class T, class U> void rd(pair<T, U> &p) {
    return rd(p.first), rd(p.second);
}
template <size_t N = 0, typename T> void rd_tuple(T &t) {
    if constexpr (N < std::tuple_size<T>::value) {
        auto &x = std::get<N>(t);
        rd(x);
        rd_tuple<N + 1>(t);
    }
}
template <class... T> void rd(tuple<T...> &tpl) {
    rd_tuple(tpl);
}

template <size_t N = 0, typename T> void rd(array<T, N> &x) {
    for (auto &d : x)
        rd(d);
}
template <class T> void rd(vector<T> &x) {
    for (auto &d : x)
        rd(d);
}

void read() {}
template <class H, class... T> void read(H &h, T &...t) {
    rd(h), read(t...);
}

void wt(const char c) {
    if (por == SZ)
        flush();
    obuf[por++] = c;
}
void wt(const string s) {
    for (char c : s)
        wt(c);
}
void wt(const char *s) {
    size_t len = strlen(s);
    for (size_t i = 0; i < len; i++)
        wt(s[i]);
}

template <typename T> void wt_integer(T x) {
    if (por > SZ - 100)
        flush();
    if (x < 0) {
        obuf[por++] = '-', x = -x;
    }
    int outi;
    for (outi = 96; x >= 10000; outi -= 4) {
        memcpy(out + outi, pre.num[x % 10000], 4);
        x /= 10000;
    }
    if (x >= 1000) {
        memcpy(obuf + por, pre.num[x], 4);
        por += 4;
    } else if (x >= 100) {
        memcpy(obuf + por, pre.num[x] + 1, 3);
        por += 3;
    } else if (x >= 10) {
        int q = (x * 103) >> 10;
        obuf[por] = q | '0';
        obuf[por + 1] = (x - q * 10) | '0';
        por += 2;
    } else
        obuf[por++] = x | '0';
    memcpy(obuf + por, out + outi + 4, 96 - outi);
    por += 96 - outi;
}

template <typename T> void wt_real(T x) {
    ostringstream oss;
    oss << fixed << setprecision(15) << double(x);
    string s = oss.str();
    wt(s);
}

void wt(int x) {
    wt_integer(x);
}
void wt(ll x) {
    wt_integer(x);
}
void wt(i128 x) {
    wt_integer(x);
}
void wt(uint x) {
    wt_integer(x);
}
void wt(ull x) {
    wt_integer(x);
}
void wt(u128 x) {
    wt_integer(x);
}
void wt(double x) {
    wt_real(x);
}
void wt(long double x) {
    wt_real(x);
}

template <class T, class U> void wt(const pair<T, U> val) {
    wt(val.first);
    wt(' ');
    wt(val.second);
}
template <size_t N = 0, typename T> void wt_tuple(const T t) {
    if constexpr (N < std::tuple_size<T>::value) {
        if constexpr (N > 0) {
            wt(' ');
        }
        const auto x = std::get<N>(t);
        wt(x);
        wt_tuple<N + 1>(t);
    }
}
template <class... T> void wt(tuple<T...> tpl) {
    wt_tuple(tpl);
}
template <class T, size_t S> void wt(const array<T, S> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}
template <class T> void wt(const vector<T> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}

void print() {
    wt('\n');
}
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    wt(head);
    if (sizeof...(Tail))
        wt(' ');
    print(forward<Tail>(tail)...);
}
void __attribute__((destructor)) _d() {
    flush();
}
} // namespace fastio


using fastio::flush;
using fastio::print;
using fastio::read;

inline void first(bool i = true) {
    print(i ? "first" : "second");
}
inline void Alice(bool i = true) {
    print(i ? "Alice" : "Bob");
}
inline void Takahashi(bool i = true) {
    print(i ? "Takahashi" : "Aoki");
}
inline void yes(bool i = true) {
    print(i ? "yes" : "no");
}
inline void Yes(bool i = true) {
    print(i ? "Yes" : "No");
}
inline void No() {
    print("No");
}
inline void YES(bool i = true) {
    print(i ? "YES" : "NO");
}
inline void NO() {
    print("NO");
}
inline void Yay(bool i = true) {
    print(i ? "Yay!" : ":(");
}
inline void Possible(bool i = true) {
    print(i ? "Possible" : "Impossible");
}
inline void POSSIBLE(bool i = true) {
    print(i ? "POSSIBLE" : "IMPOSSIBLE");
}

/**
 * @brief Fast IO
 */

namespace Random {
mt19937_64 randgen(chrono::steady_clock::now().time_since_epoch().count());
using u64 = unsigned long long;
u64 get() {
    return randgen();
}
template <typename T> T get(T L) { // [0,L]

    return get() % (L + 1);
}
template <typename T> T get(T L, T R) { // [L,R]

    return get(R - L) + L;
}
double uniform() {
    return double(get(1000000000)) / 1000000000;
}
string str(int n) {
    string ret;
    rep(i, 0, n) ret += get('a', 'z');
    return ret;
}
template <typename Iter> void shuffle(Iter first, Iter last) {
    if (first == last)
        return;
    int len = 1;
    for (auto it = first + 1; it != last; it++) {
        len++;
        int j = get(0, len - 1);
        if (j != len - 1)
            iter_swap(it, first + j);
    }
}
template <typename T> vector<T> select(int n, T L, T R) { // [L,R]

    if (n * 2 >= R - L + 1) {
        vector<T> ret(R - L + 1);
        iota(ALL(ret), L);
        shuffle(ALL(ret));
        ret.resize(n);
        return ret;
    } else {
        unordered_set<T> used;
        vector<T> ret;
        while (SZ(used) < n) {
            T x = get(L, R);
            if (!used.count(x)) {
                used.insert(x);
                ret.push_back(x);
            }
        }
        return ret;
    }
}

void relabel(int n, vector<pair<int, int>> &es) {
    shuffle(ALL(es));
    vector<int> ord(n);
    iota(ALL(ord), 0);
    shuffle(ALL(ord));
    for (auto &[u, v] : es)
        u = ord[u], v = ord[v];
}
template <bool directed, bool simple> vector<pair<int, int>> genGraph(int n) {
    vector<pair<int, int>> cand, es;
    rep(u, 0, n) rep(v, 0, n) {
        if (simple and u == v)
            continue;
        if (!directed and u > v)
            continue;
        cand.push_back({u, v});
    }
    int m = get(SZ(cand));
    vector<int> ord;
    if (simple)
        ord = select(m, 0, SZ(cand) - 1);
    else {
        rep(_, 0, m) ord.push_back(get(SZ(cand) - 1));
    }
    for (auto &i : ord)
        es.push_back(cand[i]);
    relabel(n, es);
    return es;
}
vector<pair<int, int>> genTree(int n) {
    vector<pair<int, int>> es;
    rep(i, 1, n) es.push_back({get(i - 1), i});
    relabel(n, es);
    return es;
}
}; // namespace Random


/**
 * @brief Random
 */

class GeneralWeightedMatching{
    struct E{int u,v; ll w;};
    int n,m,in;
    vector<vector<E>> G;
    vector<int> mate,slack,root,par,isS,used;
    vector<vector<int>> flower,belong;
    vector<ll> dual;
    queue<int> que;
    
    ll dist(const E& e){return dual[e.u]+dual[e.v]-e.w;}
    void update(int u,int v){
        if(!slack[v] or dist(G[u][v])<dist(G[slack[v]][v]))slack[v]=u;
    }
    void recalc(int v){
        slack[v]=0;
        rep(i,1,n+1)if(G[i][v].w and root[i]!=v and isS[root[i]]==1)update(i,v);
    }
    void push(int v){
        if(v<=n)que.push(v);
        else for(auto& nxt:flower[v])push(nxt);
    }
    void set(int v,int rt){
        root[v]=rt;
        if(v>n)for(auto& nxt:flower[v])set(nxt,rt);
    }
    int findeven(int b,int v){
        int pos=find(ALL(flower[b]),v)-flower[b].begin();
        if(pos&1){
            reverse(flower[b].begin()+1,flower[b].end());
            pos=flower[b].size()-pos;
        }
        return pos;
    }
    void match(int u,int v){
        mate[u]=G[u][v].v;
        if(u>n){
            int x=belong[u][G[u][v].u];
            int pos=findeven(u,x);
            rep(i,0,pos)match(flower[u][i],flower[u][i^1]);
            match(x,v);
            rotate(flower[u].begin(),flower[u].begin()+pos,flower[u].end());
        }
    }
    void link(int u,int v){
        for(;;){
            int nv=root[mate[u]];
            match(u,v);
            if(!nv)break;
            v=nv,u=root[par[nv]];
            match(v,u);
        }
    }
    void make(int u,int v,int lca){
        int id=n+1;
        while(id<=m and root[id])id++;
        if(id>m)m++;
        flower[id].clear();
        rep(i,1,m+1)G[id][i].w=G[i][id].w=0;
        rep(i,1,n+1)belong[id][i]=0;
        isS[id]=1,dual[id]=0,mate[id]=mate[lca];
        while(u!=lca){
            flower[id].push_back(u);
            u=root[mate[u]];
            push(u);
            flower[id].push_back(u);
            u=root[par[u]];
        }
        flower[id].push_back(lca);
        reverse(ALL(flower[id]));
        while(v!=lca){
            flower[id].push_back(v);
            v=root[mate[v]];
            push(v);
            flower[id].push_back(v);
            v=root[par[v]];
        }
        set(id,id);
        for(auto& c:flower[id]){
            rep(i,1,m+1)if(!G[id][i].w or dist(G[c][i])<dist(G[id][i])){
                G[id][i]=G[c][i],G[i][id]=G[i][c];
            }
            rep(i,1,n+1)if(belong[c][i])belong[id][i]=c;
        }
        recalc(id);
    }
    void expand(int b){
        for(auto& c:flower[b])set(c,c);
        int x=belong[b][G[b][par[b]].u];
        isS[x]=2,par[x]=par[b];
        int pos=findeven(b,x);
        for(int i=0;i<pos;i+=2){
            int T=flower[b][i],S=flower[b][i+1];
            isS[S]=1,isS[T]=2;
            par[T]=G[S][T].u;
            slack[S]=slack[T]=0;
            push(S);
        }
        rep(i,pos+1,flower[b].size()){
            isS[flower[b][i]]=0;
            recalc(flower[b][i]);
        }
        flower[b].clear();
        root[b]=0;
    }
    bool path(const E& e){
        int u=root[e.u],v=root[e.v];
        if(!isS[v]){
            par[v]=e.u;
            isS[v]=2;
            int nu=root[mate[v]];
            slack[v]=slack[nu]=0;
            isS[nu]=1;
            push(nu);
        }
        else if(isS[v]==1){
            int lca=0,bu=u,bv=v;
            in++;
            while(bu){
                used[bu]=in;
                bu=root[mate[bu]];
                if(bu)bu=root[par[bu]];
            }
            while(bv){
                if(used[bv]==in){
                    lca=bv;
                    break;
                }
                bv=root[mate[bv]];
                if(bv)bv=root[par[bv]];
            }
            if(lca)make(u,v,lca);
            else{
                link(u,v),link(v,u);
                return true;
            }
        }
        return false;
    }
    bool augment(){
        isS=slack=par=vector<int>(n*2);
        que=queue<int>();
        rep(i,1,m+1)if(root[i]==i and !mate[i]){
            isS[i]=1;
            push(i);
        }
        if(que.empty())return false;
        for(;;){
            while(!que.empty()){
                int v=que.front();
                que.pop();
                rep(i,1,n+1)if(G[v][i].w and root[i]!=root[v]){
                    if(!dist(G[v][i])){
                        if(path(G[v][i]))return true;
                    }
                    else if(isS[root[i]]!=2)update(v,root[i]);
                }
            }
            ll delta=INF;
            rep(i,n+1,m+1)if(root[i]==i and isS[i]==2)chmin(delta,dual[i]/2);
            rep(i,1,m+1)if(root[i]==i and slack[i] and isS[i]!=2){
                if(!isS[i])chmin(delta,dist(G[slack[i]][i]));
                else chmin(delta,dist(G[slack[i]][i])/2);
            }
            rep(i,1,n+1){
                if(isS[root[i]]==1){
                    dual[i]-=delta;
                    if(dual[i]<=0)return false;
                }
                else if(isS[root[i]]==2)dual[i]+=delta;
            }
            rep(i,n+1,m+1)if(root[i]==i and isS[i]){
                if(isS[i]==1)dual[i]+=delta*2;
                else dual[i]-=delta*2;
            }
            rep(i,1,m+1)if(root[i]==i and slack[i] and root[slack[i]]!=i){
                if(dist(G[slack[i]][i])==0 and path(G[slack[i]][i]))return true;
            }
            rep(i,n+1,m+1)if(root[i]==i and isS[i]==2 and dual[i]==0)expand(i);
        }
    }
public:
    GeneralWeightedMatching(int _n):n(_n),m(n),in(0),G(n*2,vector<E>(n*2)),
        mate(n*2),root(n*2),used(n*2),flower(n*2),belong(n*2,vector<int>(n*2)),dual(n*2){
        rep(i,0,n+1){
            root[i]=i;
            belong[i][i]=i;
            if(i)dual[i]=INF;
            rep(j,0,n+1)G[i][j]=E{i,j,0};
        }
    }
    void add_edge(int u,int v,ll w){
        u++,v++;
        chmax(G[u][v].w,w*2);
        chmax(G[v][u].w,w*2);
    }
    vector<int> run(){
        while(augment());
        vector<int> res(n,-1);
        rep(i,1,n+1)if(mate[i])res[i-1]=mate[i]-1;
        return res;
    }
};

/**
 * @brief General Weighted Matching
*/

int main() {
while(1) {
    int N;
    cin >> N;
    if (N == 0) return 0;
    vector<vector<int>> X(N,vector<int>(3));
    GeneralWeightedMatching G(N*2);
    rep(i,0,N) {
        rep(j,0,3) cin >> X[i][j];
        sort(X[i].begin(), X[i].end());
    }
    rep(i,0,N) {
        rep(j,0,N) {
            bool check = true;
            rep(k,0,3) if (X[i][k] >= X[j][k]) check = false;
            if (check) G.add_edge(i,j+N,X[i][0]*X[i][1]*X[i][2]);
        }
    }
    int ANS = 0;
    rep(i,0,N) ANS += X[i][0]*X[i][1]*X[i][2];
    auto Match = G.run();
    rep(i,0,N) if (Match[i] != -1) ANS -= X[i][0]*X[i][1]*X[i][2];
    cout << ANS << endl;
}
}
accuracy: 1
time_ms: 20
memory_kb: 6496
final_rank: 18

code_snippet: #if 1
// clang-format off
#include <bits/stdc++.h>

using namespace std;
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using lf = long double;
using pll = pair<ll, ll>;
#define vec vector
template <class T> using v = vector<T>;
template <class T> using vv = v<v<T>>;
template <class T> using vvv = v<vv<T>>;
using vl = v<ll>;
using vvl = vv<ll>;
using vvvl = vvv<ll>;
using vpl = v<pll>;
using vs = v<string>;
using vb = v<bool>;
using vvb = v<vb>;
using vvvb = v<vvb>;
template<class T> using PQ = priority_queue<T, v<T>, greater<T>>;
constexpr ll TEN(int n) { return (n == 0) ? 1 : 10 * TEN(n - 1); }

#define FOR(i, a, b) for (ll i = (ll)(a); i < (ll)(b); i++)
#define rep(i, N) for (ll i = 0; i < (ll)(N); i++)
#define rep1(i, N) for (ll i = 1; i <= (ll)(N); i++)
#define rrep(i, N) for (ll i = N - 1; i >= 0; i--)
#define rrep1(i, N) for (ll i = N; i > 0; i--)
#define fore(i, a) for (auto &i : a)
#define fs first
#define sc second
#define eb emplace_back
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#define UNIQUE(x) (x).erase(unique((x).begin(), (x).end()), (x).end());
#define YES(x) cout << ((x) ? "YES\n" : "NO\n");
#define Yes(x) cout << ((x) ? "Yes\n" : "No\n");
#define yes(x) cout << ((x) ? "yes\n" : "no\n");
template <class T, class U> void chmin(T &t, const U &u) { if (t > u) t = u; }
template <class T, class U> void chmax(T &t, const U &u) { if (t < u) t = u; }
template <class T> T min(const v<T> &lis) { return *min_element(all(lis)); }
template <class T> T max(v<T> &lis) { return *max_element(all(lis)); }
const int inf = (1 << 30);
const ll infl = (1LL << 60);
const ll mod93 = 998244353;
const ll mod17 = 1000000007;
int popcnt(uint x) { return __builtin_popcount(x); }
int popcnt(ull x) { return __builtin_popcountll(x); }
// 桁数
int bsr(uint x) { return 31 - __builtin_clz(x); }
int bsr(ull x) { return 63 - __builtin_clzll(x); }
// 2で割れる回数
int bsf(uint x) { return __builtin_ctz(x); }
int bsf(ull x) { return __builtin_ctzll(x); }

template <class T, class S> istream &operator>>(istream &is, pair<T, S> &x) { return is >> x.first >> x.second; }
template <class T, class S> ostream &operator<<(ostream &os, const pair<T, S> &x) { return os << x.first << " " << x.second; }
template <class T> istream &operator>>(istream &is, vector<T> &x) { for (auto &y : x) is >> y; return is; }
template <class T> ostream &operator<<(ostream &os, const vector<T> &x) {
  for (size_t i = 0, size = x.size(); i < size; i++)
    os << x[i] << (i == size - 1 ? "" : " ");
  return os;
}

ll rand_int(ll l, ll r) {  // [l, r]
  static random_device rd;
  static mt19937 gen(rd());
  return uniform_int_distribution<ll>(l, r)(gen);
}

// #include <boost/multiprecision/cpp_int.hpp>
// using cpp_int = boost::multiprecision::cpp_int;

// clang-format on
#endif

// #define _GLIBCXX_DEBUG

namespace atcoder {

template <class Cap, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    mcf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap, cost});
        g[to].push_back(_edge{from, from_id, 0, -cost});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{
            pos[i].first, _e.to, _e.cap + _re.cap, _re.cap, _e.cost,
        };
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result(m);
        for (int i = 0; i < m; i++) {
            result[i] = get_edge(i);
        }
        return result;
    }

    std::pair<Cap, Cost> flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
        return slope(s, t, flow_limit).back();
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
        return slope(s, t, std::numeric_limits<Cap>::max());
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);
        // variants (C = maxcost):
        // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
        // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge
        std::vector<Cost> dual(_n, 0), dist(_n);
        std::vector<int> pv(_n), pe(_n);
        std::vector<bool> vis(_n);
        auto dual_ref = [&]() {
            std::fill(dist.begin(), dist.end(),
                      std::numeric_limits<Cost>::max());
            std::fill(pv.begin(), pv.end(), -1);
            std::fill(pe.begin(), pe.end(), -1);
            std::fill(vis.begin(), vis.end(), false);
            struct Q {
                Cost key;
                int to;
                bool operator<(Q r) const { return key > r.key; }
            };
            std::priority_queue<Q> que;
            dist[s] = 0;
            que.push(Q{0, s});
            while (!que.empty()) {
                int v = que.top().to;
                que.pop();
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                // dist[v] = shortest(s, v) + dual[s] - dual[v]
                // dist[v] >= 0 (all reduced cost are positive)
                // dist[v] <= (n-1)C
                for (int i = 0; i < int(g[v].size()); i++) {
                    auto e = g[v][i];
                    if (vis[e.to] || !e.cap) continue;
                    // |-dual[e.to] + dual[v]| <= (n-1)C
                    // cost <= C - -(n-1)C + 0 = nC
                    Cost cost = e.cost - dual[e.to] + dual[v];
                    if (dist[e.to] - dist[v] > cost) {
                        dist[e.to] = dist[v] + cost;
                        pv[e.to] = v;
                        pe[e.to] = i;
                        que.push(Q{dist[e.to], e.to});
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                // dual[v] = dual[v] - dist[t] + dist[v]
                //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) + (shortest(s, v) + dual[s] - dual[v])
                //         = - shortest(s, t) + dual[t] + shortest(s, v)
                //         = shortest(s, v) - shortest(s, t) >= 0 - (n-1)C
                dual[v] -= dist[t] - dist[v];
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost>> result;
        result.push_back({flow, cost});
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = pv[v]) {
                c = std::min(c, g[pv[v]][pe[v]].cap);
            }
            for (int v = t; v != s; v = pv[v]) {
                auto& e = g[pv[v]][pe[v]];
                e.cap -= c;
                g[v][e.rev].cap += c;
            }
            Cost d = -dual[s];
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }

  private:
    int _n;

    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder

using namespace atcoder;

struct Solver {
  void solve(ll n) {
    using tup = tuple<ll, ll, ll, ll>;
    v<tup> sxyz(n);
    ll ans = 0;
    rep(i, n) {
      vl xyz(3);
      cin >> xyz;
      sort(all(xyz));
      ll x = xyz[0];
      ll y = xyz[1];
      ll z = xyz[2];
      sxyz[i] = {x * y * z, x, y, z};
      ans += x * y * z;
    }
    sort(all(sxyz));

    mcf_graph<ll, ll> g(2 * n + 2);
    rep(i, n) {
      auto [si, xi, yi, zi] = sxyz[i];
      FOR(j, i + 1, n) {
        auto [sj, xj, yj, zj] = sxyz[j];
        if (xi < xj && yi < yj && zi < zj)
          g.add_edge(i, n + j, 1, TEN(6) - si);
      }
    }
    rep(i, n) {
      g.add_edge(2 * n, i, 1, 0);
      g.add_edge(n + i, 2 * n + 1, 1, 0);
    }
    pll flow = g.flow(2 * n, 2 * n + 1);

    ans -= TEN(6) * flow.fs - flow.sc;
    cout << ans << "\n";
  }
};

int main() {
  cin.tie(nullptr);
  ios::sync_with_stdio(false);
  cout << fixed << setprecision(20);

  ll t = 1;
  // cin >> t;
  while (t) {
    ll n;
    cin >> n;
    if (n == 0) break;
    Solver solver;
    solver.solve(n);
  }
}
accuracy: 1
time_ms: 10
memory_kb: 3828
final_rank: 11

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (long long i = 0; i < (long long)(n); i++)
#define rrep(i,start,end) for (long long i = start;i >= (long long)(end);i--)
#define repn(i,end) for(long long i = 0; i <= (long long)(end); i++)
#define reps(i,start,end) for(long long i = start; i < (long long)(end); i++)
#define repsn(i,start,end) for(long long i = start; i <= (long long)(end); i++)
#define each(p,a) for(auto &p:a)
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef vector<long long> vll;
typedef vector<pair<long long ,long long>> vpll;
typedef vector<vector<long long>> vvll;
typedef set<ll> sll;
typedef map<long long , long long> mpll;
typedef pair<long long ,long long> pll;
typedef tuple<long long , long long , long long> tpl3;
#define LL(...) ll __VA_ARGS__; input(__VA_ARGS__)
#define LD(...) ld __VA_ARGS__; input(__VA_ARGS__)
#define Str(...) string __VA_ARGS__; input(__VA_ARGS__)
#define Ch(...) char __VA_ARGS__; input(__VA_ARGS__)
#define all(a)  (a).begin(),(a).end()
#define UNIQUE(v) v.erase( unique(v.begin(), v.end()), v.end() );
#define sz(x) (int)x.size()
// << std::fixed << std::setprecision(10)
const ll INF = 1LL << 60;
const ld EPS = 1e-9;
 
inline ll lfloor(ll x,ll m){return (x - ((x % m+ m)%m))/m;}
inline ll positive_mod(ll a,ll m){return (a % m + m)%m;}
inline ll popcnt(ull a){ return __builtin_popcountll(a);}
template<class T> bool chmin(T& a, T b){if(a > b){a = b;return true;}return false;}
template<class T> bool chmax(T& a, T b){if(a < b){a = b;return true;}return false;}
template<typename T> std::istream &operator>>(std::istream&is,std::vector<T>&v){for(T &in:v){is>>in;}return is;}
template<typename T> std::ostream &operator<<(std::ostream&os,const std::vector<T>&v){for(auto it=std::begin(v);it!=std::end(v);){os<<*it<<((++it)!=std::end(v)?" ":"");}return os;}
template<typename T1, typename T2>std::ostream &operator<< (std::ostream &os, std::pair<T1,T2> p){os << "{" << p.first << "," << p.second << "}";return os;}
template<class... T>void input(T&... a){(cin >> ... >> a);}
void print(){cout << endl;}
template<class T, class... Ts>void print(const T& a, const Ts&... b){cout << a;((cout << ' ' << b), ...);cout << endl;}
template<class T> void pspace(const T& a){ cout << a << ' ';}
void perr(){cerr << endl;}
template<class T, class... Ts>void perr(const T& a, const Ts&... b){cerr << a;((cerr << ' ' << b), ...);cerr << endl;}
void yes(bool i = true){ return print(i?"yes":"no"); }
void Yes(bool i = true){ return print(i?"Yes":"No"); }
void YES(bool i = true){ return print(i?"YES":"NO"); }
template <class T> vector<T> &operator++(vector<T> &v) {for(auto &e : v) e++;return v;}
template <class T> vector<T> operator++(vector<T> &v, signed) {auto res = v;for(auto &e : v) e++;return res;}
template <class T> vector<T> &operator--(vector<T> &v) {for(auto &e : v) e--;return v;}
template <class T> vector<T> operator--(vector<T> &v, signed) {auto res = v;for(auto &e : v) e--;return res;}
//grid探索用
vector<ll> _ta = {0,0,1,-1,1,1,-1,-1};
vector<ll> _yo = {1,-1,0,0,1,-1,1,-1};
bool isin(ll now_i,ll now_j,ll h,ll w){return (0<=now_i && now_i < h && 0 <= now_j && now_j < w);}
  
ll lpow(ll x,ll n){ll ans = 1;while(n >0){if(n & 1)ans *= x;x *= x;n >>= 1;}return ans;}
ll Modlpow(ll x,ll n,ll m){ll ans = 1;ll a = x%m;while(n >0){if(n & 1){ans *= a;ans%= m;}a *= a;a %= m;n >>= 1;}return ans;} 
const ll MOD9 = 998244353LL;
const ll MOD10 = 1000000007LL;

// for AOJ or ICPC or etc..
// 全部が0だったらtrueを返す
template<class Tp> bool zero (const Tp &x) {return x == 0;}
template<class Tp, class... Args> bool zero (const Tp &x, const Args& ...args) {return zero(x) and zero(args...);}

//参考: 蟻本P.203
//verify https://onlinejudge.u-aizu.ac.jp/solutions/problem/GRL_6_B/review/9361927/T21140031/C++17
struct Edge {
 ll to,cap,cost,rev;
};
struct MincostFlow{
  ll siz = 0;//頂点数
  vector<vector<Edge>> G;
  vector<ll> h;//ポテンシャル
  vector<ll> dist;//最短距離
  vector<ll> prevv,preve;//直前の頂点と辺
  using P = pair<ll,ll>;
  MincostFlow(ll n){
    siz = n;
    G.resize(siz);h.resize(siz);dist.resize(siz);
    prevv.resize(siz);preve.resize(siz);
  }

  //fromからtoへ容量cap,コストcostの辺を追加
  void add_edge(ll from,ll to, ll cap,ll cost){
    G[from].push_back((Edge){to,cap,cost,(ll)G[to].size()});
    G[to].push_back((Edge){from,0,-cost,(ll)G[from].size()-1});
  }

  //sからtへの流量fの最小費用流
  //流せないなら-1
  //{流せた流量,かかったコスト}を返す
  P min_cost_flow(ll s,ll t,ll f){
    ll retf = 0,retcost = 0;//流れた流量とかかったコスト
    h.assign(siz,0LL);
    while(f > 0){//ダイクストラでhを更新
      priority_queue<P,vector<P>,greater<P>> pq;
      dist.assign(siz,INF);
      dist[s] = 0;
      pq.push({0,s});
      while(!pq.empty()){
        P now = pq.top();pq.pop();
        ll v = now.second;
        if(dist[v] < now.first) continue;
        for(ll i = 0;i < (ll)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;
            pq.push({dist[e.to],e.to});
          }
        }
      }
      if(dist[t] == INF){//流せなかった
        return {0,-1};
      }
      for(ll i = 0;i < siz;i++)h[i] += dist[i];
      //s->tの最短経路に沿って目一杯流す
      ll d = f;
      for(ll v = t;v != s;v = prevv[v]){
        d = min(d,G[prevv[v]][preve[v]].cap);
      }
      f -= d;
      retf += d;
      retcost += d * h[t];
      for(ll v = t;v != s;v = prevv[v]){
        Edge &e = G[prevv[v]][preve[v]];
        e.cap -= d;
        G[v][e.rev].cap += d;
      }
    } 
    return {retf,retcost};
  }
};


// 変数をちゃんと全部受け取る！
void solve(ll n){
  vvll xyz(n,vll(3));
  vll V(n);
    ll sum = 0;
  rep(i,n){
    cin >> xyz[i];
    V[i] = xyz[i][0] *xyz[i][1]*xyz[i][2];
    sum += V[i];
  }


  MincostFlow mcf(2 * n + 2);
  ll B = lpow(10,7);


  //iはjに入れられるか
  rep(i,n){
    rep(j,n)if(i != j){
      vll ord(3);iota(all(ord),0);
      bool able = false;
      do{
        bool ok = true;
        rep(z,3){
          if(xyz[i][ord[z]] >= xyz[j][z]){
            ok = false;
          }
        }
        if(ok)able = true;
      }while(next_permutation(all(ord)));

      if(able){
        mcf.add_edge(i,n + j,1,B - V[i]);
      }
    }
  }
  rep(i,n){
    mcf.add_edge(2*n,i,1,0);
    mcf.add_edge(n+i,2*n+1,1,0);
  }
  mcf.add_edge(2*n,2*n+1,INF,B);

  cout << sum -(B*(n-1) -  mcf.min_cost_flow(2*n,2*n+1,n-1).second) << endl;

}
 
int main(){
  ios::sync_with_stdio(false);cin.tie(nullptr);
  while(true){
    LL(n);//変数数調整
    if(zero(n))break;
    solve(n);
  }
}
accuracy: 1
time_ms: 20
memory_kb: 3688
final_rank: 12

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vll = vector<ll>;
#define all(A) A.begin(),A.end()
#define rep(i, n) for (int i = 0; i < (int) (n); i++)

ll INF=5e17;
struct MCF{
    struct Edge{
        int to;
        ll cap,cost;
        int rev;
        Edge(int to,ll cap,ll cost,int rev):to(to),cap(cap),cost(cost),rev(rev){};
    };
    int V;
    vector<vector<Edge>> G;
    ll add;

    MCF()=default;
    explicit MCF(int V):V(V),G(V),add(0){}

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

    ll flow(int s,int t,ll f){
        ll ret=add;
        vector<ll> dist(V);
        vector<int> prevv(V),preve(V);
        using P=pair<ll,ll>;
        priority_queue<P,vector<P>,greater<P>> pq;

        vector<ll> h(V);

        while(f>0){
            fill(all(dist),INF);
            dist[s]=0;
            pq.emplace(0,s);
            while(!pq.empty()){
                ll d;
                int v;
                tie(d,v)=pq.top();
                pq.pop();
                if(dist[v]<d)continue;
                for(int i=0;i<(int)G[v].size();++i){
                    Edge &e=G[v][i];
                    ll ndist=dist[v]+e.cost+h[v]-h[e.to];
                    if(e.cap>0&&dist[e.to]>ndist){
                        dist[e.to]=ndist;
                        prevv[e.to]=v;
                        preve[e.to]=i;
                        pq.emplace(dist[e.to],e.to);
                    }
                }
            }
            if(dist[t]==INF)return -1;
            for(int v=0;v<V;++v)h[v]+=dist[v];

            ll d=f;
            for(int v=t;v!=s;v=prevv[v]){
                d=min(d,G[prevv[v]][preve[v]].cap);
            }
            f-=d;
            ret+=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 ret;
    }
    
};
    


void solve(ll N){
    vector<vll> A(N,vll(3));
    rep(i,N){
        rep(j,3)cin>>A[i][j];
        sort(all(A[i]));
    }

    MCF C(N*2+2);
    ll S=N*2;
    ll T=S+1;
    ll an=0;
    rep(i,N){
        C.add_edge(S,i,1,0);
        C.add_edge(i+N,T,1,0);
        ll VOL=A[i][0]*A[i][1]*A[i][2];
        C.add_edge(i,T,1,VOL);
    }
    rep(i,N)rep(j,N){
        if(A[i][0]<A[j][0]&&A[i][1]<A[j][1]&&A[i][2]<A[j][2]){
            // cout<<A[i][0]<<"@"<<A[j][0]<<endl;
            // cout<<i<<" "<<j<<endl;
            C.add_edge(i,j+N,1,0);
        }
    }
    cout<<C.flow(S,T,N)<<endl;
}

int main() {

    cin.tie(nullptr);
    ios::sync_with_stdio(false);

    ll N;
    while(cin>>N){
        if(N==0)return 0;
        solve(N);
    }


}
accuracy: 1
time_ms: 10
memory_kb: 3772
final_rank: 10

code_snippet: #line 1 "2828.test.cpp"
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/2828"

#line 2 "/home/zawatin/compro/cp-documentation/Src/Graph/Flow/SuccessiveShortestPath.hpp"

#line 2 "/home/zawatin/compro/cp-documentation/Src/Template/TypeAlias.hpp"

#include <cstdint>
#include <cstddef>

namespace zawa {

using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i128 = __int128_t;

using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;

using usize = std::size_t;

} // namespace zawa
#line 2 "/home/zawatin/compro/cp-documentation/Src/Utility/U32Pair.hpp"

#line 4 "/home/zawatin/compro/cp-documentation/Src/Utility/U32Pair.hpp"

#include <functional>
#include <iostream>

namespace zawa {

class U32Pair {
private:
    static constexpr u32 SHIFT{32};
    static constexpr u32 MASK{static_cast<u32>((1LL << SHIFT) - 1)};
    u64 value_{};
public:
    constexpr U32Pair() {}
    constexpr U32Pair(u32 first, u32 second) {
        value_ = (static_cast<u64>(first) << SHIFT) | second;
    }
    constexpr u32 first() const noexcept {
        return static_cast<u32>(value_ >> SHIFT);
    }
    constexpr u32 second() const noexcept {
        return static_cast<u32>(value_ & MASK);
    }
    constexpr u64 combined() const noexcept {
        return value_;
    }
    constexpr U32Pair& operator=(const U32Pair& rhs) {
        value_ = rhs.value_;
        return *this;
    }
    friend constexpr bool operator==(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ == rhs.value_;
    }
    friend constexpr bool operator!=(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ != rhs.value_;
    }
    friend constexpr bool operator<(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ < rhs.value_;
    }
    friend constexpr bool operator<=(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ <= rhs.value_;
    }
    friend constexpr bool operator>(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ > rhs.value_;
    }
    friend constexpr bool operator>=(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ >= rhs.value_;
    }
    friend std::ostream& operator<<(std::ostream& os, const U32Pair& pair) {
        os << '(' << pair.first() << ',' << pair.second() << ')';
        return os;
    }
};

struct U32PairHash {
    usize operator()(const U32Pair& pair) const noexcept {
        return std::hash<u64>{}(pair.combined());
    }
};

} // namespace zawa
#line 5 "/home/zawatin/compro/cp-documentation/Src/Graph/Flow/SuccessiveShortestPath.hpp"

#include <cassert>
#include <limits>
#include <queue>
#include <type_traits>
#include <utility>
#include <vector>

namespace zawa {

template <class Cap, class Cost>
class SuccessiveShortestPath {
public:
    static_assert(std::is_signed_v<Cost>, U"Cost must be signed");
    static constexpr Cost invalid{(std::numeric_limits<Cost>::max() >> 1) - 1};
    static constexpr u32 reverseId(u32 i) noexcept {
        return i ^ 1;
    }

    struct Edge {
        u32 from{}, to{};
        Cap residual{};
        Cost cost{};
        Edge() = default;
        Edge(u32 from, u32 to, const Cap& cap, const Cost& cost)
            : from{from}, to{to}, residual{cap}, cost{cost} {}
    };

    usize n_{}, m_{};
    std::vector<Edge> edges_;
    std::vector<std::vector<u32>> g_;
    std::vector<Cost> dist_, potential_;
    std::vector<U32Pair> prev_;
    Cost mcf_{};

    constexpr usize size() const noexcept {
        return n_;
    }
    constexpr usize edgeSize() const noexcept {
        return m_;
    }
    
    SuccessiveShortestPath() = default;
    SuccessiveShortestPath(usize n, usize m = usize{}) 
        : n_{n}, m_{}, edges_{}, g_(n), dist_(n), potential_(n), prev_(n), mcf_{} {
        g_.shrink_to_fit();
        dist_.shrink_to_fit();
        potential_.shrink_to_fit();
        prev_.shrink_to_fit();
        edges_.reserve(2 * m);
    }

    void emplace(u32 from, u32 to, const Cap& cap, const Cost& cost) {
        g_[from].emplace_back(m_);
        edges_.emplace_back(from, to, cap, cost);
        m_++;
    }

    u32 addEdge(u32 from, u32 to, const Cap& cap, const Cost& cost) {
        assert(from < size());
        assert(to < size());
        u32 res{static_cast<u32>(m_)};
        emplace(from, to, cap, cost);
        emplace(to, from, Cap{}, -cost);
        return res;
    }

    inline u32 from(u32 i) const noexcept {
        return edges_[i].from;
    }
    inline u32 to(u32 i) const noexcept {
        return edges_[i].to;
    }
    inline const Cap& residual(u32 i) const noexcept {
        return edges_[i].residual;
    }
    inline const Cost& cost(u32 i) const noexcept {
        return edges_[i].cost;
    }
    inline const Cap& flowed(u32 i) const noexcept {
        assert(i < edgeSize());
        return residual(i ^ 1);
    }
    inline const Cap& capcacity(u32 i) const noexcept {
        assert(i < edgeSize());
        return residual(i) + flowed(i);
    }

    inline Cost edgeCost(u32 i) const noexcept {
        return potential_[from(i)] + cost(i) - potential_[to(i)];
    }
    bool relax(u32 i) {
        if (residual(i) > 0 and dist_[to(i)] > dist_[from(i)] + edgeCost(i)) {
            dist_[to(i)] = dist_[from(i)] + edgeCost(i);
            prev_[to(i)] = U32Pair{from(i), i};
            return true;
        }
        return false;
    }

    bool dijkstra(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        std::fill(dist_.begin(), dist_.end(), invalid);
        dist_[s] = Cost{};
        using qt = std::pair<Cost, u32>;
        std::priority_queue<qt, std::vector<qt>, std::greater<qt>> que;
        que.emplace(dist_[s], s);
        while (que.size()) {
            auto [d, v]{que.top()};
            que.pop();
            if (dist_[v] < d) continue;
            for (u32 i : g_[v]) {
                if (relax(i)) {
                    que.emplace(dist_[to(i)], to(i));
                }
            }
        }
        return dist_[t] < invalid;
    }

    bool dagdp(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        std::fill(dist_.begin(), dist_.end(), invalid);
        dist_[s] = Cost{};
        std::vector<u32> ord;
        ord.reserve(size());
        std::vector<bool> vis(size());
        vis[s] = true;
        ord.push_back(s);
        for (u32 i{} ; i < ord.size() ; i++) {
            u32 v{ord[i]};
            for (auto e : g_[v]) {
                if (!vis[to(e)]) {
                    ord.push_back(to(e));
                    vis[to(e)] = true;
                }
                relax(e);
            }
        }
        return dist_[t] < invalid;
    }

    bool bellmanford(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        std::fill(dist_.begin(), dist_.end(), invalid);
        dist_[s] = Cost{};
        for (u32 i{} ; i + 1 < size() ; i++) {
            for (u32 j{} ; j < edgeSize() ; j++) {
                relax(j);
            }
        }
        return dist_[t] < invalid;
    }

    void updatePotential() {
        for (u32 v{} ; v < size() ; v++) {
            potential_[v] = potential_[v] + (dist_[v] == invalid ? Cost{} : dist_[v]);
        }
    }

    Cap flush(u32 s, u32 t, Cap up = std::numeric_limits<Cap>::max()) {
        for (u32 v{t} ; v != s ; v = prev_[v].first()) {
            up = std::min(up, residual(prev_[v].second()));
        }
        for (u32 v{t} ; v != s ; v = prev_[v].first()) {
            edges_[prev_[v].second()].residual -= up;
            edges_[prev_[v].second() ^ 1].residual += up;
        }
        return up;
    }

    bool flow(u32 s, u32 t, Cap flow) {
        assert(s < size());
        assert(t < size());
        while (flow > 0 and dijkstra(s, t)) {
            updatePotential();
            Cap water{flush(s, t, flow)};
            mcf_ += potential_[t] * water;
            flow -= water;
        }
        return flow == 0;
    }

    Cap maxflow(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        Cap flow{};
        while (dijkstra(s, t)) {
            updatePotential();
            Cap water{flush(s, t)};
            mcf_ += potential_[t] * water;
            flow += water;
        }
        return flow;
    }

    std::vector<Cost> slope(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        Cap flow{};
        std::vector<Cost> res;
        while (dijkstra(s, t)) {
            updatePotential();
            Cap water{flush(s, t)};
            for (u32 i{} ; i < water ; i++) {
                res.emplace_back(mcf_);
                mcf_ += potential_[t];
                flow++;
            }
        }
        res.emplace_back(mcf_);
        return res;
    }

    struct Line {
        Cap dn{}, up{};
        Cost slope{};
        Line() = default;
        Line(Cap dn, Cap up, Cost slope) : dn{dn}, up{up}, slope{slope} {}
    };
    std::vector<Line> slopeACL(u32 s, u32 t) {
        assert(s < size());
        assert(t < size()); 
        Cap flow{};
        std::vector<Line> res;
        while (dijkstra(s, t)) {
            updatePotential();
            Cap water{flush(s, t)};
            mcf_ += potential_[t] * water;
            res.emplace_back(flow, flow + water, potential_[t]);
            flow += water;
        }
        return res;
    }

    Cost minCost() const noexcept {
        return mcf_;
    }
};

} // namespace zawa
#line 2 "/home/zawatin/compro/cp-documentation/Src/Template/IOSetting.hpp"

#line 4 "/home/zawatin/compro/cp-documentation/Src/Template/IOSetting.hpp"

#line 6 "/home/zawatin/compro/cp-documentation/Src/Template/IOSetting.hpp"
#include <iomanip>

namespace zawa {

void SetFastIO() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
}

void SetPrecision(u32 dig) {
    std::cout << std::fixed << std::setprecision(dig);
}

} // namespace zawa
#line 5 "2828.test.cpp"

#include <algorithm>
#line 8 "2828.test.cpp"
#include <numeric>

bool solve() {
    using namespace zawa;
    int n; std::cin >> n;
    if (n == 0) return false;
    std::vector<std::array<int, 3>> a(n);
    for (auto& x : a) {
        for (auto& v : x) std::cin >> v;
        std::sort(x.begin(), x.end());
    }
    SuccessiveShortestPath<int, int> mcf(2 * n + 2);
    int source{2 * n}, sink{source + 1};
    for (int i{} ; i < n ; i++) {
        mcf.addEdge(source, i, 1, 0);
        mcf.addEdge(n + i, sink, 1, 0);
    }
    std::vector<int> big(n);
    for (int i{} ; i < n ; i++) {
        big[i] = a[i][0] * a[i][1] * a[i][2];
    }
    for (int i{} ; i < n ; i++) {
        for (int j{} ; j < n ; j++) {
            bool ok{true};
            for (int k{} ; k < 3 ; k++) ok &= a[i][k] > a[j][k];
            if (ok) {
                mcf.addEdge(i, n + j, 1, -big[j]);
            }
        }
    }
    mcf.dagdp(source, sink);
    mcf.updatePotential();
    int sum{std::accumulate(big.begin(), big.end(), 0)};
    int ans{sum};
    for (const auto& flow : mcf.slope(source, sink)) {
        ans = std::min(ans, sum + flow);
    }
    std::cout << ans << '\n';
    return true;
}

int main() {
    while (solve()) ;
}
accuracy: 1
time_ms: 10
memory_kb: 3652
final_rank: 5

code_snippet: #include <bits/stdc++.h>




namespace zawa {

using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i128 = __int128_t;

using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;

using usize = std::size_t;

} // namespace zawa


namespace zawa {

void SetFastIO() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
}

void SetPrecision(u32 dig) {
    std::cout << std::fixed << std::setprecision(dig);
}

} // namespace zawa




namespace zawa {

class U32Pair {
private:
    static constexpr u32 SHIFT{32};
    static constexpr u32 MASK{static_cast<u32>((1LL << SHIFT) - 1)};
    u64 value_{};
public:
    constexpr U32Pair() {}
    constexpr U32Pair(u32 first, u32 second) {
        value_ = (static_cast<u64>(first) << SHIFT) | second;
    }
    constexpr u32 first() const noexcept {
        return static_cast<u32>(value_ >> SHIFT);
    }
    constexpr u32 second() const noexcept {
        return static_cast<u32>(value_ & MASK);
    }
    constexpr u64 combined() const noexcept {
        return value_;
    }
    constexpr U32Pair& operator=(const U32Pair& rhs) {
        value_ = rhs.value_;
        return *this;
    }
    friend constexpr bool operator==(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ == rhs.value_;
    }
    friend constexpr bool operator!=(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ != rhs.value_;
    }
    friend constexpr bool operator<(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ < rhs.value_;
    }
    friend constexpr bool operator<=(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ <= rhs.value_;
    }
    friend constexpr bool operator>(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ > rhs.value_;
    }
    friend constexpr bool operator>=(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ >= rhs.value_;
    }
    friend std::ostream& operator<<(std::ostream& os, const U32Pair& pair) {
        os << '(' << pair.first() << ',' << pair.second() << ')';
        return os;
    }
};

struct U32PairHash {
    usize operator()(const U32Pair& pair) const noexcept {
        return std::hash<u64>{}(pair.combined());
    }
};

} // namespace zawa

#include <type_traits>

namespace zawa {

template <class Cap, class Cost>
class SuccessiveShortestPath {
public:
    static_assert(std::is_signed_v<Cost>, U"Cost must be signed");
    static constexpr Cost invalid{(std::numeric_limits<Cost>::max() >> 1) - 1};
    static constexpr u32 reverseId(u32 i) noexcept {
        return i ^ 1;
    }

    struct Edge {
        u32 from{}, to{};
        Cap residual{};
        Cost cost{};
        Edge() = default;
        Edge(u32 from, u32 to, const Cap& cap, const Cost& cost)
            : from{from}, to{to}, residual{cap}, cost{cost} {}
    };

    usize n_{}, m_{};
    std::vector<Edge> edges_;
    std::vector<std::vector<u32>> g_;
    std::vector<Cost> dist_, potential_;
    std::vector<U32Pair> prev_;
    Cost mcf{};

    constexpr usize size() const noexcept {
        return n_;
    }
    constexpr usize edgeSize() const noexcept {
        return m_;
    }
    
    SuccessiveShortestPath() = default;
    SuccessiveShortestPath(usize n, usize m = usize{}) 
        : n_{n}, m_{}, edges_{}, g_(n), dist_(n), potential_(n), prev_(n), mcf{} {
        g_.shrink_to_fit();
        dist_.shrink_to_fit();
        potential_.shrink_to_fit();
        prev_.shrink_to_fit();
        edges_.reserve(2 * m);
    }

    void emplace(u32 from, u32 to, const Cap& cap, const Cost& cost) {
        g_[from].emplace_back(m_);
        edges_.emplace_back(from, to, cap, cost);
        m_++;
    }

    u32 addEdge(u32 from, u32 to, const Cap& cap, const Cost& cost) {
        assert(from < size());
        assert(to < size());
        u32 res{static_cast<u32>(m_)};
        emplace(from, to, cap, cost);
        emplace(to, from, Cap{}, -cost);
        return res;
    }

    inline u32 from(u32 i) const noexcept {
        return edges_[i].from;
    }
    inline u32 to(u32 i) const noexcept {
        return edges_[i].to;
    }
    inline const Cap& residual(u32 i) const noexcept {
        return edges_[i].residual;
    }
    inline const Cost& cost(u32 i) const noexcept {
        return edges_[i].cost;
    }
    inline const Cap& flowed(u32 i) const noexcept {
        assert(i < edgeSize());
        return residual(i ^ 1);
    }
    inline const Cap& capcacity(u32 i) const noexcept {
        assert(i < edgeSize());
        return residual(i) + flowed(i);
    }

    inline Cost edgeCost(u32 i) const noexcept {
        return potential_[from(i)] + cost(i) - potential_[to(i)];
    }
    bool relax(u32 i) {
        if (residual(i) > 0 and dist_[to(i)] > dist_[from(i)] + edgeCost(i)) {
            dist_[to(i)] = dist_[from(i)] + edgeCost(i);
            prev_[to(i)] = U32Pair{from(i), i};
            return true;
        }
        return false;
    }

    bool dijkstra(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        std::fill(dist_.begin(), dist_.end(), invalid);
        dist_[s] = Cost{};
        using qt = std::pair<Cost, u32>;
        std::priority_queue<qt, std::vector<qt>, std::greater<qt>> que;
        que.emplace(dist_[s], s);
        while (que.size()) {
            auto [d, v]{que.top()};
            que.pop();
            if (dist_[v] < d) continue;
            for (u32 i : g_[v]) {
                if (relax(i)) {
                    que.emplace(dist_[to(i)], to(i));
                }
            }
        }
        return dist_[t] < invalid;
    }

    bool dagdp(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        std::fill(dist_.begin(), dist_.end(), invalid);
        dist_[s] = Cost{};
        std::vector<u32> ord;
        ord.reserve(size());
        std::vector<bool> vis(size());
        vis[s] = true;
        ord.push_back(s);
        for (u32 i{} ; i < ord.size() ; i++) {
            u32 v{ord[i]};
            for (auto e : g_[v]) {
                if (!vis[to(e)]) {
                    ord.push_back(to(e));
                    vis[to(e)] = true;
                }
                relax(e);
            }
        }
        return dist_[t] < invalid;
    }

    bool bellmanford(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        std::fill(dist_.begin(), dist_.end(), invalid);
        dist_[s] = Cost{};
        for (u32 i{} ; i + 1 < size() ; i++) {
            for (u32 j{} ; j < edgeSize() ; j++) {
                relax(j);
            }
        }
        return dist_[t] < invalid;
    }


    Cap flush(u32 s, u32 t, Cap up = std::numeric_limits<Cap>::max()) {
        for (u32 v{t} ; v != s ; v = prev_[v].first()) {
            up = std::min(up, residual(prev_[v].second()));
        }
        for (u32 v{t} ; v != s ; v = prev_[v].first()) {
            edges_[prev_[v].second()].residual -= up;
            edges_[prev_[v].second() ^ 1].residual += up;
        }
        return up;
    }

    bool flow(u32 s, u32 t, Cap flow) {
        assert(s < size());
        assert(t < size());
        while (flow > 0 and dijkstra(s, t)) {
            for (u32 i{} ; i < size() ; i++) {
                potential_[i] = potential_[i] + (dist_[i] == invalid ? Cost{} : dist_[i]);
            }
            Cap water{flush(s, t, flow)};
            mcf += potential_[t] * water;
            flow -= water;
        }
        return flow == 0;
    }

    Cap maxflow(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        Cap flow{};
        while (dijkstra(s, t)) {
            for (u32 i{} ; i < size() ; i++) {
                potential_[i] = potential_[i] + (dist_[i] == invalid ? Cost{} : dist_[i]);
            }
            Cap water{flush(s, t)};
            mcf += potential_[t] * water;
            flow += water;
        }
        return flow;
    }

    struct Line {
        Cap dn{}, up{};
        Cost slope{};
        Line() = default;
        Line(Cap dn, Cap up, Cost slope) : dn{dn}, up{up}, slope{slope} {}
    };
    std::vector<Line> slope(u32 s, u32 t) {
        assert(s < size());
        assert(t < size()); 
        Cap flow{};
        std::vector<Line> res;
        while (dijkstra(s, t)) {
            for (u32 i{} ; i < size() ; i++) {
                potential_[i] = potential_[i] + (dist_[i] == invalid ? Cost{} : dist_[i]);
            }
            Cap water{flush(s, t)};
            mcf += potential_[t] * water;
            res.emplace_back(flow, flow + water, potential_[t]);
            flow += water;
        }
        return res;
    }

    Cost minCost() const noexcept {
        return mcf;
    }
};

} // namespace zawa
using namespace zawa;

bool solve() {
    int n; std::cin >> n;
    if (n == 0) return false;
    using data = std::array<int, 3>;
    std::vector<data> a(n);
    for (auto& x : a) {
        for (auto& v : x) std::cin >> v;
        std::sort(x.begin(), x.end());
    }
    std::vector<int> big(n);
    for (int i{} ; i < n ; i++) {
        big[i] = a[i][0] * a[i][1] * a[i][2];
    }
    SuccessiveShortestPath<int, int> mcf(2 * n + 2);
    int source{2 * n}, sink{2 * n + 1};
    for (int i{} ; i < n ; i++) {
        mcf.addEdge(source, i, 1, 0);
        mcf.addEdge(n + i, sink, 1, 0);
    }
    for (int i{} ; i < n ; i++) {
        for (int j{} ; j < n ; j++) if (i != j) {
            bool ok{true};
            for (int k{} ; k < 3 ; k++) ok &= a[i][k] > a[j][k];
            if (ok) {
                mcf.addEdge(i, n + j, 1, -big[j]);
            }
        }
    }
    // mcf.dagdp(source, sink);
    // for (int i{} ; i < (int)mcf.size() ; i++) {
    //     mcf.potential_[i] = mcf.potential_[i] + (mcf.dist_[i] == mcf.invalid ? 0 : mcf.dist_[i]);
    // }
    mcf.maxflow(source, sink);
    int ans{std::accumulate(big.begin(), big.end(), 0) + mcf.minCost()};
    std::cout << ans << '\n';
    return true;
}

int main() {
    SetFastIO();
    for (int i{} ; solve() ; i++) {
        // std::cerr << "case " << i << " end##############################\n";
    }
}
accuracy: 1
time_ms: 10
memory_kb: 3580
final_rank: 3

code_snippet: #include <bits/stdc++.h>




namespace zawa {

using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i128 = __int128_t;

using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;

using usize = std::size_t;

} // namespace zawa


namespace zawa {

void SetFastIO() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
}

void SetPrecision(u32 dig) {
    std::cout << std::fixed << std::setprecision(dig);
}

} // namespace zawa




namespace zawa {

class U32Pair {
private:
    static constexpr u32 SHIFT{32};
    static constexpr u32 MASK{static_cast<u32>((1LL << SHIFT) - 1)};
    u64 value_{};
public:
    constexpr U32Pair() {}
    constexpr U32Pair(u32 first, u32 second) {
        value_ = (static_cast<u64>(first) << SHIFT) | second;
    }
    constexpr u32 first() const noexcept {
        return static_cast<u32>(value_ >> SHIFT);
    }
    constexpr u32 second() const noexcept {
        return static_cast<u32>(value_ & MASK);
    }
    constexpr u64 combined() const noexcept {
        return value_;
    }
    constexpr U32Pair& operator=(const U32Pair& rhs) {
        value_ = rhs.value_;
        return *this;
    }
    friend constexpr bool operator==(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ == rhs.value_;
    }
    friend constexpr bool operator!=(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ != rhs.value_;
    }
    friend constexpr bool operator<(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ < rhs.value_;
    }
    friend constexpr bool operator<=(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ <= rhs.value_;
    }
    friend constexpr bool operator>(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ > rhs.value_;
    }
    friend constexpr bool operator>=(const U32Pair& lhs, const U32Pair& rhs) {
        return lhs.value_ >= rhs.value_;
    }
    friend std::ostream& operator<<(std::ostream& os, const U32Pair& pair) {
        os << '(' << pair.first() << ',' << pair.second() << ')';
        return os;
    }
};

struct U32PairHash {
    usize operator()(const U32Pair& pair) const noexcept {
        return std::hash<u64>{}(pair.combined());
    }
};

} // namespace zawa

#include <type_traits>

namespace zawa {

template <class Cap, class Cost>
class SuccessiveShortestPath {
public:
    static_assert(std::is_signed_v<Cost>, U"Cost must be signed");
    static constexpr Cost invalid{(std::numeric_limits<Cost>::max() >> 1) - 1};
    static constexpr u32 reverseId(u32 i) noexcept {
        return i ^ 1;
    }

    struct Edge {
        u32 from{}, to{};
        Cap residual{};
        Cost cost{};
        Edge() = default;
        Edge(u32 from, u32 to, const Cap& cap, const Cost& cost)
            : from{from}, to{to}, residual{cap}, cost{cost} {}
    };

    usize n_{}, m_{};
    std::vector<Edge> edges_;
    std::vector<std::vector<u32>> g_;
    std::vector<Cost> dist_, potential_;
    std::vector<U32Pair> prev_;
    Cost mcf{};

    constexpr usize size() const noexcept {
        return n_;
    }
    constexpr usize edgeSize() const noexcept {
        return m_;
    }
    
    SuccessiveShortestPath() = default;
    SuccessiveShortestPath(usize n, usize m = usize{}) 
        : n_{n}, m_{}, edges_{}, g_(n), dist_(n), potential_(n), prev_(n), mcf{} {
        g_.shrink_to_fit();
        dist_.shrink_to_fit();
        potential_.shrink_to_fit();
        prev_.shrink_to_fit();
        edges_.reserve(2 * m);
    }

    void emplace(u32 from, u32 to, const Cap& cap, const Cost& cost) {
        g_[from].emplace_back(m_);
        edges_.emplace_back(from, to, cap, cost);
        m_++;
    }

    u32 addEdge(u32 from, u32 to, const Cap& cap, const Cost& cost) {
        assert(from < size());
        assert(to < size());
        u32 res{static_cast<u32>(m_)};
        emplace(from, to, cap, cost);
        emplace(to, from, Cap{}, -cost);
        return res;
    }

    inline u32 from(u32 i) const noexcept {
        return edges_[i].from;
    }
    inline u32 to(u32 i) const noexcept {
        return edges_[i].to;
    }
    inline const Cap& residual(u32 i) const noexcept {
        return edges_[i].residual;
    }
    inline const Cost& cost(u32 i) const noexcept {
        return edges_[i].cost;
    }
    inline const Cap& flowed(u32 i) const noexcept {
        assert(i < edgeSize());
        return residual(i ^ 1);
    }
    inline const Cap& capcacity(u32 i) const noexcept {
        assert(i < edgeSize());
        return residual(i) + flowed(i);
    }

    inline Cost edgeCost(u32 i) const noexcept {
        return potential_[from(i)] + cost(i) - potential_[to(i)];
    }
    bool relax(u32 i) {
        if (residual(i) > 0 and dist_[to(i)] > dist_[from(i)] + edgeCost(i)) {
            dist_[to(i)] = dist_[from(i)] + edgeCost(i);
            prev_[to(i)] = U32Pair{from(i), i};
            return true;
        }
        return false;
    }

    bool dijkstra(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        std::fill(dist_.begin(), dist_.end(), invalid);
        dist_[s] = Cost{};
        using qt = std::pair<Cost, u32>;
        std::priority_queue<qt, std::vector<qt>, std::greater<qt>> que;
        que.emplace(dist_[s], s);
        while (que.size()) {
            auto [d, v]{que.top()};
            que.pop();
            if (dist_[v] < d) continue;
            for (u32 i : g_[v]) {
                if (relax(i)) {
                    que.emplace(dist_[to(i)], to(i));
                }
            }
        }
        return dist_[t] < invalid;
    }

    bool dagdp(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        std::fill(dist_.begin(), dist_.end(), invalid);
        dist_[s] = Cost{};
        std::vector<u32> ord;
        ord.reserve(size());
        std::vector<bool> vis(size());
        vis[s] = true;
        ord.push_back(s);
        for (u32 i{} ; i < ord.size() ; i++) {
            u32 v{ord[i]};
            for (auto e : g_[v]) {
                if (!vis[to(e)]) {
                    ord.push_back(to(e));
                    vis[to(e)] = true;
                }
                relax(e);
            }
        }
        return dist_[t] < invalid;
    }

    bool bellmanford(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        std::fill(dist_.begin(), dist_.end(), invalid);
        dist_[s] = Cost{};
        for (u32 i{} ; i + 1 < size() ; i++) {
            for (u32 j{} ; j < edgeSize() ; j++) {
                relax(j);
            }
        }
        return dist_[t] < invalid;
    }


    Cap flush(u32 s, u32 t, Cap up = std::numeric_limits<Cap>::max()) {
        for (u32 v{t} ; v != s ; v = prev_[v].first()) {
            up = std::min(up, residual(prev_[v].second()));
        }
        for (u32 v{t} ; v != s ; v = prev_[v].first()) {
            edges_[prev_[v].second()].residual -= up;
            edges_[prev_[v].second() ^ 1].residual += up;
        }
        return up;
    }

    bool flow(u32 s, u32 t, Cap flow) {
        assert(s < size());
        assert(t < size());
        while (flow > 0 and dijkstra(s, t)) {
            for (u32 i{} ; i < size() ; i++) {
                potential_[i] = potential_[i] + (dist_[i] == invalid ? Cost{} : dist_[i]);
            }
            Cap water{flush(s, t, flow)};
            mcf += potential_[t] * water;
            flow -= water;
        }
        return flow == 0;
    }

    Cap maxflow(u32 s, u32 t) {
        assert(s < size());
        assert(t < size());
        Cap flow{};
        while (dijkstra(s, t)) {
            for (u32 i{} ; i < size() ; i++) {
                potential_[i] = potential_[i] + (dist_[i] == invalid ? Cost{} : dist_[i]);
            }
            Cap water{flush(s, t)};
            mcf += potential_[t] * water;
            flow += water;
        }
        return flow;
    }

    struct Line {
        Cap dn{}, up{};
        Cost slope{};
        Line() = default;
        Line(Cap dn, Cap up, Cost slope) : dn{dn}, up{up}, slope{slope} {}
    };
    std::vector<Line> slope(u32 s, u32 t) {
        assert(s < size());
        assert(t < size()); 
        Cap flow{};
        std::vector<Line> res;
        while (dijkstra(s, t)) {
            for (u32 i{} ; i < size() ; i++) {
                potential_[i] = potential_[i] + (dist_[i] == invalid ? Cost{} : dist_[i]);
            }
            Cap water{flush(s, t)};
            mcf += potential_[t] * water;
            res.emplace_back(flow, flow + water, potential_[t]);
            flow += water;
        }
        return res;
    }

    Cost minCost() const noexcept {
        return mcf;
    }
};

} // namespace zawa
using namespace zawa;

bool solve() {
    int n; std::cin >> n;
    if (n == 0) return false;
    using data = std::array<int, 3>;
    std::vector<data> a(n);
    for (auto& x : a) {
        for (auto& v : x) std::cin >> v;
        std::sort(x.begin(), x.end());
    }
    std::vector<int> big(n);
    for (int i{} ; i < n ; i++) {
        big[i] = a[i][0] * a[i][1] * a[i][2];
    }
    SuccessiveShortestPath<int, int> mcf(2 * n + 2);
    int source{2 * n}, sink{2 * n + 1};
    for (int i{} ; i < n ; i++) {
        mcf.addEdge(source, i, 1, 0);
        mcf.addEdge(n + i, sink, 1, 0);
    }
    for (int i{} ; i < n ; i++) {
        for (int j{} ; j < n ; j++) if (i != j) {
            bool ok{true};
            for (int k{} ; k < 3 ; k++) ok &= a[i][k] > a[j][k];
            if (ok) {
                mcf.addEdge(i, n + j, 1, -big[j]);
            }
        }
    }
    mcf.dagdp(source, sink);
    for (int i{} ; i < (int)mcf.size() ; i++) {
        mcf.potential_[i] = mcf.potential_[i] + (mcf.dist_[i] == mcf.invalid ? 0 : mcf.dist_[i]);
    }
    mcf.maxflow(source, sink);
    int ans{std::accumulate(big.begin(), big.end(), 0) + mcf.minCost()};
    std::cout << ans << '\n';
    return true;
}

int main() {
    SetFastIO();
    for (int i{} ; solve() ; i++) {
        // std::cerr << "case " << i << " end##############################\n";
    }
}
accuracy: 1
time_ms: 10
memory_kb: 3580
final_rank: 3

code_snippet: #include <bits/stdc++.h>
using namespace std;

#ifdef _RUTHEN
#include <debug.hpp>
#else
#define show(...) true
#endif

using ll = long long;
#define REP(i, n) for (int i = 0; i < (n); i++)
template <class T> using V = vector<T>;

struct ssp {
    using C = int;
    struct edge {
        int from;
        int to;
        C cap;
        C cost;
        int rev;
    };
    int n;
    int s, t;
    vector<vector<edge>> g;
    ssp(int n, int s, int t) : n(n), s(s), t(t), g(n) {}
    void add_edge(int from, int to, int cap, int cost) {
        g[from].push_back({from, to, cap, cost, (int)(g[to].size())});
        g[to].push_back({to, from, 0, -cost, (int)(g[from].size() - 1)});
    }
    vector<int> level;
    vector<int> iter;

    C min_cost_flow(C f) {
        vector<C> po(n, 0);
        C ans = 0;
        auto sel = [&](const edge& e) { return e.cost + po[e.from] - po[e.to]; };
        while (f > 0) {
            vector<C> dist(n, 0);
            vector<bool> vis(n, false);
            vector<int> p(n, -1);
            vector<int> pe(n, -1);
            using P = pair<C, int>;
            priority_queue<P, vector<P>, greater<P>> que;
            que.push({dist[s], s});
            vis[s] = true;
            while (!que.empty()) {
                int v = que.top().second;
                C d = que.top().first;
                que.pop();
                if (dist[v] < d) continue;
                for (int i = 0; i < g[v].size(); i++) {
                    auto& e = g[v][i];
                    int u = e.to;
                    if (e.cap == 0) continue;
                    if (!vis[u] || dist[u] > dist[v] + sel(e)) {
                        vis[u] = true;
                        dist[u] = dist[v] + sel(e);
                        p[u] = v;
                        pe[u] = i;
                        que.push({dist[u], u});
                    }
                }
            }
            if (p[t] == -1) break;

            C ff = f;
            for (int u = t; u != s; u = p[u]) ff = min(ff, g[p[u]][pe[u]].cap);
            for (int u = t; u != s; u = p[u]) {
                ans += ff * g[p[u]][pe[u]].cost;
                g[p[u]][pe[u]].cap -= ff;
                g[u][g[p[u]][pe[u]].rev].cap += ff;
            }
            f -= ff;
            for (int i = 0; i < n; i++) po[i] += dist[i];
        }
        if (f != 0) return -1;
        return ans;
    }
};

void solve(int N) {
    vector mat(N, vector<int>(3));
    vector<int> vol(N, 1);
    REP(i, N) {
        REP(j, 3) cin >> mat[i][j];
        sort(mat[i].begin(), mat[i].end());
        REP(j, 3) vol[i] *= mat[i][j];
    }
    int N2 = N * 2 + 2;
    int S = N * 2, T = S + 1;
    ssp G(N2, S, T);
    auto check = [&](int i, int j) -> bool {
        // sz[i] > sz[j]
        bool res = true;
        REP(k, 3) res &= mat[i][k] > mat[j][k];
        return res;
    };
    REP(i, N) {
        REP(j, N) {
            if (i == j) {
                G.add_edge(i, T, 1, vol[i]);
            }
            if (check(i, j)) {
                G.add_edge(i, j + N, 1, vol[i] - vol[j]);
            }
        }
    }
    REP(i, N) {
        G.add_edge(S, i, 1, 0);
        G.add_edge(i + N, T, 1, 0);
    }
    auto cost = G.min_cost_flow(N);
    cout << cost << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int N;
    while (cin >> N, N != 0) solve(N);
    return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 3600
final_rank: 15

code_snippet: #include <bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
using piii = pair<int, pii>;
using ll = long long;
using vi = vector<int>;
using vll = vector<ll>;


ll hung(int n, vector<vector<ll>> &a) {
    ll inf = 0xffffffffffffffLL;
    vi p(n);
    vi way(n);
    vll u(n, 0);
    vll v(n, 0);
    vll minV;
    vector<bool> used;
    for (int i = 1; i < n; i++) {
        p[0] = i;
        minV.assign(n, inf);
        used.assign(n, false);
        int j0 = 0;
        while (p[j0] != 0) {
            int i0 = p[j0];
            int j1 = 0;
            used[j0] = true;
            ll delta = inf;
            for (int j = 1; j < n; j++) {
                if (used[j]) continue;
                ll curr = a[i0][j] - u[i0] - v[j];
                if (curr < minV[j]) {
                    minV[j] = curr;
                    way[j] = j0;
                }
                if (minV[j] < delta) {
                    delta = minV[j];
                    j1 = j;
                }
            }
            for (int j = 0; j < n; j++) {
                if (used[j]) {
                    u[p[j]] += delta;
                    v[j] -= delta;
                } else {
                    minV[j] -= delta;
                }
            }
            j0 = j1;
        }
        do {
            p[j0] = p[way[j0]];
            j0 = way[j0];
        } while (j0 != 0);
    }
    return v[0];
}

bool solve() {
    int n;
    cin >> n;
    if (n == 0) {
        return false;
    }
    vector<piii> v;
    for (int i = 0; i < n; i++) {
        int x, y, z;
        cin >> x >> y >> z;
        int nx = min(min(x, y), z);
        int nz = max(max(x, y), z);
        v.emplace_back(nx, make_pair(x+y+z-nx-nz, nz));
    }
    sort(v.begin(), v.end());
    vector<vector<ll>> a(n+1, vector<ll>(n+1, 0));
    ll ans = 0;
    for (int i = 0; i < n; i++) {
        int x = v[i].first;
        int y = v[i].second.first;
        int z = v[i].second.second;
        ans += x * y * z;
        for (int j = 0; j < n; j++) {
            int ox = v[j].first;
            int oy = v[j].second.first;
            int oz = v[j].second.second;
            if (ox < x && oy < y && oz < z) {
                a[i+1][j+1] = -ox * oy * oz;
            }
//            cout << a[i+1][j+1] << " ";
        }
//        cout << endl;
    }
    ll ret = hung(n+1, a);
    cout << ans - ret << endl;
    return true;
}

int main() {
    while (solve());
}
accuracy: 1
time_ms: 10
memory_kb: 3572
final_rank: 2

code_snippet: #include<bits/stdc++.h>
#define rep(i, N) for(int i = 0; i < N; i++)
#define int64_t long long
#define int long long

using namespace std;
using ll = long long;
using ld = long double;

const ld EPS = 1e-14;
const ll LINF = 2e18;

struct Point{
    ll x, y, z;
};

bool able(Point a, Point b){
    ll arr[3] = {b.x, b.y, b.z};
    sort(arr, arr + 3);
    do{
        if(a.x > arr[0] && a.y > arr[1] && a.z > arr[2]) return true;
    }while(next_permutation(arr, arr + 3));
    return false;
}

class min_cost_flow2{
    using edge = struct{ int to, cap, cost, rev; };
    const ll Inf = 100100100100100100;
    int N;
    vector<vector<edge>> G;
    vector<ll> dist;
    vector<int> prev_v, prev_e;

    public:
    min_cost_flow2(int n) : N(n), G(n), dist(n), prev_v(n), prev_e(n) {}

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

    // bellman-ford
    int solve(int s, int t, int f){
        int res = 0;

        while(f > 0){
            fill(dist.begin(), dist.end(), Inf);
            dist[s] = 0;
            bool update = 1;

            while(update){
                update = 0;
                for(int v = 0; v < N; v++){
                    if(dist[v] == Inf) continue;
                    for(int i = 0; i < (int)G[v].size(); i++){
                        edge &e = G[v][i];
                        if(e.cap > 0 && dist[e.to] > dist[v] + e.cost){
                            dist[e.to] = dist[v] + e.cost;
                            prev_v[e.to] = v;
                            prev_e[e.to] = i;
                            update = 1;
                        }
                    }
                }
            }

            if(dist[t] == Inf) return -1;

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

signed main(){
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);
    cout << fixed << setprecision(10);
    while(1){
        int N; cin >> N;
        if(N == 0) break;
        vector<Point> A(N);
        rep(i, N){
            auto &it = A[i];
            cin >> it.x >> it.y >> it.z;
        }

        min_cost_flow2 MCF(N * 2 + 2);
        int S = N * 2, T = N * 2 + 1;
        rep(i, N){
            rep(j, N){
                if(i == j) continue;
                if(able(A[i], A[j])){
                    //cerr << i << " " << j << endl;
                    MCF.add_edge(i + N, j, 1, -(A[j].x * A[j].y * A[j].z));
                }
            }
        }
        rep(i, N){
            MCF.add_edge(i, i + N, 1, 0);
        }
        rep(i, N){
            MCF.add_edge(S, i, 1, 0);
            MCF.add_edge(i + N, T, 1, 0);
        }
        MCF.add_edge(S, T, N, 0);

        ll ans = 0;
        rep(i, N) ans += A[i].x * A[i].y * A[i].z;
        ans += MCF.solve(S, T, N);
        cout << ans << '\n';
    }
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3708
final_rank: 7

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define rep(i,s,n) for (int i = s; i < (int)(n); i++)
#define rrep(i,s,n) for (int i = (int)(n)-1; i >= (int)(s); i--)
#define all(v) v.begin(),v.end()

using ll = long long;


namespace lib{

using namespace std;

struct MinCostFlow{
	int n; vector<vector<vector<ll>>> g;
	vector<ll> h,dis; vector<int>pv,pe;
	MinCostFlow(int v):n(v),g(v),dis(v),pv(v),pe(v){}
	void add_edge(int from, int to, ll cap, ll cost){
		g[from].push_back({to,cap,cost,(int)g[to].size()});
		g[to].push_back({from,0,-cost,(int)g[from].size()-1});
	}
	ll flow(int s, int t, ll f){
		ll res = 0;
		h.assign(n,0);
		using Q = pair<ll,int>;
		while(f>0){
			priority_queue<Q,vector<Q>,greater<Q>>que;
			dis.assign(n,LLONG_MAX);
			dis[s]=0;
			que.push({0,s});
			while(que.size()){
				Q q = que.top();
				int v= q.second;
				que.pop();
				if(dis[v]<q.first) continue;
				rep(i,0,g[v].size()){
					auto edge=g[v][i];
					int to = edge[0];
					ll cap = edge[1], cost = edge[2];
					if (cap > 0 && dis[to]>dis[v]+cost+h[v]-h[to]){
						dis[to]=dis[v]+cost+h[v]-h[to];
						pv[to]=v; pe[to]=i;
						que.push({dis[to],to});
					}
				}
			}
			if (dis[t]==LLONG_MAX)return -1;
			rep(i,0,n)h[i]+=dis[i];
			ll d=f;
			for(int i=t;i!=s; i=pv[i])d=min(d,g[pv[i]][pe[i]][1]);
			f-=d;
			res+=d*h[t];
			for (int i=t;i!=s;i=pv[i]){
				auto&edge=g[pv[i]][pe[i]];
				edge[1]-=d;
				g[i][edge[3]][1]+=d;
			}
		}
		return res;
	}
};
}

bool hantei(int i, int j, vector<vector<ll>> &x){
	rep(k,0,3){
		if(x[i][k]>=x[j][k])return false;
	}
	return true;
}

int main(){
	while(true){
		int n; cin >> n;
		if (n == 0) break;
		vector<vector<ll>> x(n, vector<ll>(3));
		ll sums = 0;
		rep(i,0,n) cin >> x[i][0] >> x[i][1] >> x[i][2];
		rep(i,0,n) sort(all(x[i]));
		rep(i,0,n) sums += x[i][0]*x[i][1]*x[i][2];
		lib::MinCostFlow mcf(2*n+2);
		ll M=1e10;
		rep(i,0,n) rep(j,0,n){
			if (i==j)continue;
			if (hantei(i,j,x)) mcf.add_edge(n+i, j, 1, 0);
		}
		rep(i,0,n){
			mcf.add_edge(2*n, n+i, 1, M-x[i][0]*x[i][1]*x[i][2]);
			mcf.add_edge(i, 2*n+1, 1, 0);
		}
		mcf.add_edge(2*n, 2*n+1, n, M);
		ll fl = mcf.flow(2*n, 2*n+1, n);
		cout << sums - ((ll)n*M - fl) << endl;
	}
}
accuracy: 1
time_ms: 50
memory_kb: 3880
final_rank: 16

code_snippet: #include <bits/stdc++.h>
using namespace std;

// edge class (for network-flow)
template<class FLOWTYPE, class COSTTYPE> struct Edge {
    int rev, from, to, id;
    FLOWTYPE cap, icap;
    COSTTYPE cost;
    Edge(int r, int f, int t, FLOWTYPE ca, COSTTYPE co, int id = -1) :
        rev(r), from(f), to(t), cap(ca), icap(ca), cost(co), id(id) {}
    friend ostream& operator << (ostream& s, const Edge& E) {
        if (E.cap > 0)
            return s << E.from << "->" << E.to <<
                '(' << E.cap << ',' << E.cost << ')';
        else return s;
    }
};

// graph class (for network-flow)
template<class FLOWTYPE, class COSTTYPE> struct Graph {
    vector<vector<Edge<FLOWTYPE, COSTTYPE> > > list;
    
    Graph(int n = 0) : list(n) { }
    void init(int n = 0) { list.clear(); list.resize(n); }
    void reset() { for (int i = 0; i < (int)list.size(); ++i) for (int j = 0; j < list[i].size(); ++j) list[i][j].cap = list[i][j].icap; }
    inline vector<Edge<FLOWTYPE, COSTTYPE> >& operator [] (int i) { return list[i]; }
    inline const size_t size() const { return list.size(); }
    
    inline Edge<FLOWTYPE, COSTTYPE> &redge(const Edge<FLOWTYPE, COSTTYPE> &e) {
        if (e.from != e.to) return list[e.to][e.rev];
        else return list[e.to][e.rev + 1];
    }
    
    void addedge(int from, int to, FLOWTYPE cap, COSTTYPE cost, int id = -1) {
        list[from].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[to].size(), from, to, cap, cost, id));
        list[to].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[from].size() - 1, to, from, 0, -cost));
    }
    
    void add_undirected_edge(int from, int to, FLOWTYPE cap, COSTTYPE cost, int id = -1) {
        list[from].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[to].size(), from, to, cap, cost, id));
        list[to].push_back(Edge<FLOWTYPE, COSTTYPE>((int)list[from].size() - 1, to, from, cap, cost, id));
    }

    friend ostream& operator << (ostream& s, const Graph& G) {
        s << endl;
        for (int i = 0; i < G.size(); ++i) {
            s << i << ":";
            for (auto e : G.list[i]) s << " " << e;
            s << endl;
        }
        return s;
    }
};

// min-cost flow (by primal-dual)
template<class FLOWTYPE, class COSTTYPE> COSTTYPE MinCostFlow
(Graph<FLOWTYPE, COSTTYPE> &G, int s, int t, FLOWTYPE f) {
    int n = (int)G.size();
    vector<COSTTYPE> dist(n, -1);
    vector<int> prevv(n), preve(n), seen(n, 0);
    COSTTYPE res = 0;
    while (f > 0) {
        seen.assign(n, 0);
        dist[s] = 0;
        seen[s] = true;
        while (true) {
            bool update = false;
            for (int v = 0; v < n; ++v) {
                if (!seen[v]) continue;
                for (int i = 0; i < G[v].size(); ++i) {
                    Edge<FLOWTYPE, COSTTYPE> &e = G[v][i];
                    if (e.cap > 0 && (!seen[e.to] || dist[e.to] > dist[v] + e.cost)) {
                        dist[e.to] = dist[v] + e.cost;
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        seen[e.to] = true;
                        update = true;
                    }
                }
            }
            if (!update) break;
        }
        if (!seen[t]) return -1;
        FLOWTYPE d = f;
        for (int v = t; v != s; v = prevv[v]) {
            d = min(d, G[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += dist[t] * d;
        for (int v = t; v != s; v = prevv[v]) {
            Edge<FLOWTYPE, COSTTYPE> &e = G[prevv[v]][preve[v]];
            Edge<FLOWTYPE, COSTTYPE> &re = G.redge(e);
            e.cap -= d;
            re.cap += d;
        }
    }
    return res;
}

#define REP(i, n) for (long long i = 0; i < (long long)(n); ++i)
#define REP2(i, a, b) for (long long i = a; i < (long long)(b); ++i)
#define COUT(x) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << endl
template<class T1, class T2> ostream& operator << (ostream &s, pair<T1,T2> P)
{ return s << '<' << P.first << ", " << P.second << '>'; }
template<class T> ostream& operator << (ostream &s, vector<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, deque<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, vector<vector<T> > P)
{ for (int i = 0; i < P.size(); ++i) { s << endl << P[i]; } return s << endl; }
template<class T> ostream& operator << (ostream &s, set<T> P)
{ for(auto it : P) { s << "<" << it << "> "; } return s; }
template<class T1, class T2> ostream& operator << (ostream &s, map<T1,T2> P)
{ for(auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s; }

int main() {
    long long N;
    while (cin >> N, N) {
        long long sum = 0;
        vector<vector<long long>> box(N, vector<long long>(3));
        for (int i = 0; i < N; ++i) {
            cin >> box[i][0] >> box[i][1] >> box[i][2];
            sort(box[i].begin(), box[i].end());
            sum += box[i][0] * box[i][1] * box[i][2];
        }
        
        // ネットワークフロー
        int source = N*2, sink = N*2+1;
        Graph<long long, long long> G(N*2+2);
        G.addedge(source, sink, N, 0);  // 流量任意にするため
        for (int i = 0; i < N; ++i) {
            G.addedge(source, i, 1, 0);
            G.addedge(i+N, sink, 1, 0);
        }
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < N; ++j) {
                if (box[i][0] > box[j][0] && box[i][1] > box[j][1] && box[i][2] > box[j][2]) {
                    G.addedge(i, j+N, 1, -box[j][0] * box[j][1] * box[j][2]);
                }
            }
        }
        
        //COUT(G);
        
        long long res = sum + MinCostFlow(G, source, sink, N);
        cout << res << endl;
    }
}
accuracy: 1
time_ms: 20
memory_kb: 3736
final_rank: 14

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i=0; i<(n); i++)
#define INF ((1LL<<62)-(1LL<<31))
#define all(a)  (a).begin(),(a).end()
#define rall(a)  (a).rbegin(),(a).rend()
typedef long long ll;
typedef pair<ll,ll> pl;

struct Graph {
    struct Edge {
        int rev,from,to; ll cap,cost;
        Edge(int r,int f,int t,ll cp,ll ct):rev(r),from(f),to(t),cap(cp),cost(ct) {}
    };
    vector<vector<Edge>> list;
    Graph(int N=0):list(N) {}
    size_t size() { return list.size(); }
    vector<Edge> &operator [] (int i) { return list[i]; }
    Edge& redge(const Edge &e) { return list[e.to][e.rev]; }
    void run_flow(Edge &e,int f) {
        e.cap-=f;
        redge(e).cap+=f;
    }
    void addedge(int from,int to,ll cap,ll cost) {
        int fromrev=(int)list[from].size();
        int torev=(int)list[to].size();
        list[from].push_back(Edge(torev,from,to,cap,cost));
        list[to].push_back(Edge(fromrev,to,from,0LL,-cost));
    }
    //Graph g(n);
};

struct Mincostflow {
    Mincostflow() {}
    int solve(Graph &G,int s,int t,ll f) {
        int gs=G.size();
        ll res=0;
        vector<ll> pot(gs),pre_v(gs),pre_e(gs);
        while(f>0) {
            vector<ll> dist(gs,INF);
            dist[s]=0;
            priority_queue<pl,vector<pl>,greater<pl>> pq;
            pq.push({0,s});
            while(!pq.empty()) {
                ll cd=pq.top().first,cv=pq.top().second; pq.pop();
                if(dist[cv]<cd) continue;
                rep(i,(int)G[cv].size()) { 
                    auto &nv=G[cv][i];
                    if(nv.cap>0&&dist[nv.to]>dist[cv]+nv.cost+pot[cv]-pot[nv.to]) {
                        dist[nv.to]=dist[cv]+nv.cost+pot[cv]-pot[nv.to];
                        pre_v[nv.to]=cv; pre_e[nv.to]=i;
                        pq.push({dist[nv.to],nv.to});
                    }
                }
            }
            if(dist[t]==INF) return -1;
            rep(i,gs) pot[i]+=dist[i];
            ll cf=f;
            for(int i=t;i!=s;i=pre_v[i]) cf=min(cf,G[pre_v[i]][pre_e[i]].cap);
            f-=cf;
            res+=cf*pot[t];
            for(int i=t;i!=s;i=pre_v[i]) G.run_flow(G[pre_v[i]][pre_e[i]],cf);
        }
        return res;
    }
    //Mincostflow mcf;
};

int main() {
    while(true) {
        int n;
        cin >> n;
        if(n==0) break;
        Graph g(2*n+2);
        vector<vector<int>> v(n,vector<int> (3));
        rep(i,n) {
            rep(j,3) cin >> v[i][j];
            sort(all(v[i]));
            g.addedge(2*n,i,1,0);
            g.addedge(i,2*n+1,1,v[i][0]*v[i][1]*v[i][2]);
            g.addedge(n+i,2*n+1,1,0);
        }
        rep(i,n) {
            rep(j,n) {
                bool flag=true;
                rep(k,3) if(v[i][k]>=v[j][k]) flag=false;
                if(flag) g.addedge(i,n+j,1,0);
            }
        }
        Mincostflow mcf;
        cout << mcf.solve(g,2*n,2*n+1,n) << endl;
    }
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3496
final_rank: 1