Problem:
MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], processEscapes: true }});

マトリョーシカ

マトリョーシカはロシアの民芸品として有名な人形である．
マトリョーシカは上下に分割でき，開くと中により小さい別の人形が入っている．
現れた小さい人形を開くとさらに小さい人形が入っている，というような入れ子構造になっている．

あなたは旅行先で珍しい形のマトリョーシカを見つけ，N 体の人形を購入した．
i 番目の人形の形状は，xi × yi × zi の直方体である．

ひとしきりマトリョーシカを鑑賞したあなたは，マトリョーシカを仕舞おうとしている．
その前に，いくつかの人形を別の人形に格納することによって必要なスペースを減らしたい．
人形を格納する際には，まだ中にひとつも人形を格納していない人形にだけ，他の人形をひとつ格納できる．
ただし，直接的に格納される人形についてだけ数えるものとし，中に人形が入っている人形を別の人形に格納することはできる．

収納された人形は，外部から見えない状態になる．
ただし，以下の条件を満たさなければならない．

人形は回転してよいが，直方体のそれぞれの辺は，他方の直方体のいずれかの辺に平行

回転後，対応する辺同士の長さそれぞれについて，収納される側の人形の長さの方が短い

1 個の人形の中に直接収納できる人形の数は高々 1 個

押入れの容積は限られているので，外部から見えている人形の体積の和を最小化したい．
あなたの仕事は，人形を収納する操作を任意の回数繰り返して達成できる，外部から見えている人形の体積の和の最小値を求めるプログラムを作成することである．

Input

入力は複数のデータセットからなる．
データセットの個数は最大でも 50 個を超えない．
各データセットは次の形式で表される．

N
x1 y1 z1
:
:
xN yN zN

各データセットは N + 1 行からなり，データセットの 1 行目には，人形の数を表す整数 N が与えられる．
続く N 行の内 i 行目には，i 番目の人形の大きさを表す 3 つの整数 xi, yi, zi が半角スペース区切りで与えられる．
これらの整数は，1 ≤ N, xi, yi, zi ≤ 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

Solution in Rust (judge verdict: Accepted)

fn main() {
    let (r, w) = (std::io::stdin(), std::io::stdout());
    let mut sc = IO::new(r.lock(), w.lock());

    loop {
        let n: usize = sc.read();
        if n == 0 {
            return;
        }
        let mut xyz = vec![];
        for _ in 0..n {
            let x: i64 = sc.read();
            let y: i64 = sc.read();
            let z: i64 = sc.read();
            let mut v = vec![x, y, z];
            v.sort();
            xyz.push(v);
        }

        let v = n * 2 + 2;
        let mut graph = primal_dual::MinimumCostFlowSolver::new(v);
        let source = v - 2;
        let sink = v - 1;
        for i in 0..n {
            graph.add_edge(source, i, 1, 0);
            graph.add_edge(i, sink, 1, xyz[i][0] * xyz[i][1] * xyz[i][2]);
            graph.add_edge(i + n, sink, 1, 0);
            for j in 0..n {
                if (0..3).all(|p| xyz[i][p] < xyz[j][p]) {
                    graph.add_edge(i, j + n, 1, 0);
                }
            }
        }
        let f = graph.solve(source, sink, n as i64).unwrap();
        println!("{}", f);
    }
}
pub mod primal_dual {
    use std::cmp;
    use std::collections::BinaryHeap;
    use std::i64;
    type Flow = i64;
    type Cost = i64;
    const INF: Cost = 1 << 50;
    struct Edge {
        to: usize,
        capacity: Flow,
        flow: Flow,
        cost: Cost,
        reverse_to: usize,
        is_reversed: bool,
    }
    impl Edge {
        fn residue(&self) -> Flow {
            self.capacity - self.flow
        }
    }

    pub struct MinimumCostFlowSolver {
        graph: Vec<Vec<Edge>>,
        previous_edge: Vec<(usize, usize)>,
    }

    impl MinimumCostFlowSolver {
        pub fn new(n: usize) -> Self {
            MinimumCostFlowSolver {
                graph: (0..n).map(|_| Vec::new()).collect(),
                previous_edge: vec![(0, 0); n],
            }
        }

        pub fn add_edge(&mut self, from: usize, to: usize, capacity: Flow, cost: Cost) {
            let reverse_from = self.graph[to].len();
            let reverse_to = self.graph[from].len();
            self.graph[from].push(Edge {
                to,
                capacity,
                flow: 0,
                cost,
                reverse_to: reverse_from,
                is_reversed: false,
            });
            self.graph[to].push(Edge {
                to: from,
                capacity,
                flow: capacity,
                cost: -cost,
                reverse_to,
                is_reversed: true,
            });
        }

        pub fn solve(&mut self, source: usize, sink: usize, mut flow: Flow) -> Option<Flow> {
            let n = self.graph.len();
            let mut result = 0;
            let mut h = vec![0; n];
            let mut q: BinaryHeap<(Cost, usize)> = BinaryHeap::new();
            while flow > 0 {
                let mut dist = vec![INF; n];
                dist[source] = 0;
                q.push((0, source));
                while let Some((current_dist, v)) = q.pop() {
                    if dist[v] < current_dist {
                        continue;
                    }
                    for (i, e) in self.graph[v].iter().enumerate() {
                        if e.residue() == 0 {
                            continue;
                        }
                        if dist[e.to] + h[e.to] > current_dist + h[v] + e.cost {
                            dist[e.to] = current_dist + h[v] + e.cost - h[e.to];
                            self.previous_edge[e.to] = (v, i);
                            q.push((dist[e.to], e.to));
                        }
                    }
                }

                if dist[sink] == INF {
                    return None;
                }

                for i in 0..n {
                    h[i] += dist[i];
                }
                let mut df = flow;
                let mut v = sink;
                while v != source {
                    let (prev_v, prev_e) = self.previous_edge[v];
                    df = cmp::min(df, self.graph[prev_v][prev_e].residue());
                    v = prev_v;
                }
                flow -= df;
                result += df * h[sink];
                let mut v = sink;
                while v != source {
                    let (prev_v, prev_e) = self.previous_edge[v];
                    self.graph[prev_v][prev_e].flow += df;
                    let reversed_edge_id = self.graph[prev_v][prev_e].reverse_to;
                    self.graph[v][reversed_edge_id].flow -= df;
                    v = prev_v;
                }
            }
            Some(result)
        }

        pub fn neg_solve(&mut self, source: usize, sink: usize, mut flow: Flow) -> Option<Flow> {
            let n = self.graph.len();
            let mut result = 0;
            while flow > 0 {
                let mut dist = vec![INF; n];
                dist[source] = 0;
                loop {
                    let mut updated = false;
                    for v in 0..n {
                        if dist[v] == INF {
                            continue;
                        }

                        for (i, e) in self.graph[v].iter().enumerate() {
                            if e.residue() == 0 {
                                continue;
                            }
                            if dist[e.to] > dist[v] + e.cost {
                                dist[e.to] = dist[v] + e.cost;
                                self.previous_edge[e.to] = (v, i);
                                updated = true;
                            }
                        }
                    }
                    if !updated {
                        break;
                    }
                }

                if dist[sink] == INF {
                    return None;
                }

                let mut df = flow;
                let mut v = sink;
                while v != source {
                    let (prev_v, prev_e) = self.previous_edge[v];
                    df = cmp::min(df, self.graph[prev_v][prev_e].residue());
                    v = prev_v;
                }
                flow -= df;
                result += df * dist[sink];
                let mut v = sink;
                while v != source {
                    let (prev_v, prev_e) = self.previous_edge[v];
                    self.graph[prev_v][prev_e].flow += df;
                    let reversed_edge_id = self.graph[prev_v][prev_e].reverse_to;
                    self.graph[v][reversed_edge_id].flow -= df;
                    v = prev_v;
                }
            }
            Some(result)
        }
    }
}

pub struct IO<R, W: std::io::Write>(R, std::io::BufWriter<W>);

impl<R: std::io::Read, W: std::io::Write> IO<R, W> {
    pub fn new(r: R, w: W) -> Self {
        IO(r, std::io::BufWriter::new(w))
    }
    pub fn write<S: ToString>(&mut self, s: S) {
        use std::io::Write;
        self.1.write_all(s.to_string().as_bytes()).unwrap();
    }
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .0
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n' || b == b'\r' || b == b'\t')
            .take_while(|&b| b != b' ' && b != b'\n' && b != b'\r' && b != b'\t')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}