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 Scala (judge verdict: Accepted)

import java.util.Scanner

import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer

object Main extends App {
  val in = new Scanner(System.in)

  def solve(n: Int): Long = {
    val xyz = (for (_ <- 0 until n) yield {
      Array(in.nextInt(), in.nextInt(), in.nextInt()).sorted
    }).toArray


    val source = n * 2
    val sink = source + 1
    val minimumCostFlow = new MinimumCostFlow(sink + 1)
    for {
      i <- 0 until n
      j <- 0 until n
      if i != j
      if xyz(i)(0) < xyz(j)(0)
      if xyz(i)(1) < xyz(j)(1)
      if xyz(i)(2) < xyz(j)(2)
    } {
      minimumCostFlow.addEdge(i, n + j, 1, 0)
    }
    for (i <- 0 until n) {
      minimumCostFlow.addEdge(source, i, 1, 0)
      minimumCostFlow.addEdge(i, sink, 1, xyz(i)(0) * xyz(i)(1) * xyz(i)(2))
      minimumCostFlow.addEdge(i + n, sink, 1, 0)
    }
    minimumCostFlow.calculateCost(source, sink, n)
  }

  def loop(): Unit = {
    while (true) {
      val n = in.nextInt()
      if (n == 0) {
        return
      }
      val ans = solve(n)
      println(ans)
    }
  }

  loop()

}

class Edge(val to: Int, var cap: Long, val cost: Long, val rev: Int)

class MinimumCostFlow(V: Int) {
  val graph: Array[ArrayBuffer[Edge]] = (for (_ <- 0 until V) yield new ArrayBuffer[Edge]()).toArray
  val prevV = new Array[Int](V)
  val prevE = new Array[Int](V)

  def addEdge(from: Int, to: Int, cap: Long, cost: Long): Unit = {
    graph(from).append(new Edge(to, cap, cost, graph(to).size))
    graph(to).append(new Edge(from, 0, -cost, graph(from).size - 1))
  }

  def calculateCost(source: Int, sink: Int, flow: Long): Long = {
    val INF = Long.MaxValue / 2
    var cost = 0L
    var residue = flow
    val potential = new Array[Long](V)
    while (residue > 0) {
      val dist = Array.fill[Long](V)(INF)
      dist(source) = 0
      val queue = mutable.PriorityQueue.empty[(Long, Int)](implicitly[Ordering[(Long, Int)]].reverse)
      queue.enqueue((0, source))
      while (queue.nonEmpty) {
        val p = queue.dequeue()
        val v = p._2
        if (dist(v) >= p._1) {
          for (i <- graph(v).indices) {
            val e = graph(v)(i)
            val u = e.to
            if (e.cap > 0 && dist(u) > dist(v) + e.cost + potential(v) - potential(u)) {
              dist(u) = dist(v) + e.cost + potential(v) - potential(u)
              prevV(u) = v
              prevE(u) = i
              queue.enqueue((dist(u), u))
            }
          }
        }
      }
      if (dist(sink) == INF) {
        return -1
      }

      for (v <- 0 until V) {
        potential(v) += dist(v)
      }
      var d = residue
      var v = sink
      while (v != source) {
        d = math.min(d, graph(prevV(v))(prevE(v)).cap)
        v = prevV(v)
      }
      residue -= d
      cost += d * potential(sink)

      v = sink
      while (v != source) {
        val e = graph(prevV(v))(prevE(v))
        e.cap -= d
        graph(v)(e.rev).cap += d
        v = prevV(v)
      }
    }
    return cost
  }
}