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

K: AOR イカちゃんの成績

問題

AOR イカちゃんは $N$ 回のレポートの点数のみで成績が決まる授業を受けている。

AOR イカちゃんは同じ授業を受けている友達が $M$ 人いて、自分が苦手なテーマのレポートは、そのテーマが得意な友達のレポートを写すことで、その友達と同じ点数を取ることができる。

ただし、他人のレポートを写していることを先生に気付かれてはいけないので、 $N$ 回のうち少なくとも $K$ 回は他人のレポートを写さず、自力でレポートを仕上げなくてはならない。
また、 AOR イカちゃんは友達に迷惑をかけたくないと思ったので友達 $i$ に対してレポートを写すのは $T_i$ 回以下にすることにした。

AOR イカちゃんが自力でレポートを仕上げたときの点数と、友達がレポートを仕上げた時の点数が与えられたときに、 AOR イカちゃんが取ることのできる合計点数の最大値を答えよ。

制約

$1 \le N \le 100$

$0 \le M \le 100$

$0 \le K \le N$

$0 \le a_i \le 100$

$0 \le b_{ij} \le 100$

$0 \le T_i \le N$

入力

入力は以下の形式で標準入力から与えられる。

$N \ M \ K$

$a_1 \ \cdots \ a_N$

$b_{11} \ \cdots \ b_{1N}$

$\vdots$

$b_{M1} \ \cdots \ b_{MN}$

$T_1 \ \cdots \ T_M$

$a_i$ はAORイカちゃんが $i$ 番目のレポートを仕上げた時の点数を、$b_{ij}$ は友達 $i$ が $j$ 番目のレポートを仕上げた時の点数を表す。

出力

AOR イカちゃんが取ることのできる合計点数の最大値を出力せよ。また、末尾に改行も出力せよ。

サンプル

入力例 1

3 2 2
50 65 70
80 100 80
90 65 45
1 1

出力例 1

225

AOR イカちゃんは 1 回だけ他人のレポートを写すことができるので、友達 2 の 1 つめのレポートを写すことで、 AOR イカちゃんは最高点数を取ることができる。

入力例 2

3 0 3
0 0 0

出力例 2

0

AOR イカちゃんには友達がいないため、レポートを写すことはできない。

Solution in Scala (judge verdict: Accepted)

import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.reflect.ClassTag

object Main extends App {

  import scala.io.StdIn._

  implicit object G extends Group[Int] {
    override def plus(a: Int, b: Int): Int = a + b

    override def minus(a: Int, b: Int): Int = a - b

    override def times(a: Int, b: Int): Int = a * b

    override def maxSentinel: Int = Int.MaxValue

    override def zero: Int = 0
  }
  solve
  def solve: Unit = {
    val Array(n, m, k) = readLine.trim.split(' ').map(_.toInt)
    val ikaScore = readLine.trim.split(' ').map(_.toInt)
    if (m == 0) {
      println(ikaScore.sum)
    } else {
      val friendsScores = Array.tabulate(m) { _ ⇒ readLine.trim.split(' ').map(_.toInt) }
      val t = readLine.trim.split(' ').map(_.toInt)
      val edges = ArrayBuffer[(Int, Int, Int, Int)]()
      for (i ← friendsScores.indices) {
        edges.append((m + n, i, t(i), 0))
      }
      for (i ← friendsScores.indices; j ← ikaScore.indices if ikaScore(j) < friendsScores(i)(j)) {
        edges.append((i, m + j, 1, ikaScore(j) - friendsScores(i)(j)))
      }
      for (j ← ikaScore.indices) {
        edges.append((m + j, m + n + 1, 1, 0))
      }
      println(ikaScore.sum - minCostFlow(m + n, m + n + 1, m + n + 2, edges.toArray, n - k))
    }
  }
  implicit class Extend[T](value: T){
    def let[R](func: T ⇒ R): R = func(value)
    def also(func: T ⇒ Unit): T = {func(value); value}
  }

  trait Group[N]{
    def plus(a: N, b: N): N
    def minus(a: N, b: N): N
    def times(a: N, b: Int): N
    def maxSentinel: N
    def zero: N
  }

  private case class Edge[N](to: Int, capacity: Int, pair: Int, cost: N)
  //edge => (from, to, capacity, cost)
  def minCostFlow[N : ClassTag](source: Int, drain: Int, size: Int, edge: Array[(Int, Int, Int, N)], flowLimit: Int = Int.MaxValue)(implicit m: Group[N], o: Ordering[N]): N = {
    val edges = makeGraph(size, edge)
    val potential = Array.tabulate(size){_ ⇒ m.maxSentinel}
    for (e ← edges) for (Edge(to, cap, _, c) ← e if cap > 0 && o.compare(potential(to), c) > 0) potential(to) = c
    potential(source) = m.zero
    potential(drain) = m.zero
    val prevEdge = Array.tabulate(size){_ ⇒ (Edge(-1, -1, -1, m.zero), Int.MaxValue)}
    val minCost = Array.tabulate(size){_ ⇒ m.maxSentinel}
    val queue = mutable.PriorityQueue()(Ordering.by[(Int, N), N](_._2).reverse)
    var canMatch = true
    var result = m.zero
    var restFlow = flowLimit
    while(canMatch) {
      canMatch = false
      queue.enqueue((source, m.zero))
      minCost.indices.foreach(i ⇒ minCost(i) = m.maxSentinel)
      minCost(source) = m.zero
      while(queue.nonEmpty){
        val (prev, cost) = queue.dequeue()
        if (prev != drain && o.compare(minCost(prev), cost) == 0){
          for (e@Edge(to, cap, pair, c) ← edges(prev) if cap > 0){
            //minCost(to) > potential(prev) + c - potential(to) + cost
            if (o.equiv(minCost(to), m.maxSentinel) || o.compare(m.minus(m.minus(minCost(to), potential(prev)), cost), m.minus(c, potential(to))) > 0) {
              minCost(to) = m.plus(m.minus(potential(prev), potential(to)), m.plus(c, cost))
              prevEdge(to) = (edges(to)(pair), math.min(prevEdge(prev)._2, cap))
              queue.enqueue((to, minCost(to)))
            }
          }
        }
      }
      if (minCost(drain) != m.maxSentinel && o.gt(m.zero, minCost(drain))) {
        canMatch = true
        var last = drain
        val bottleNeck = prevEdge(drain)._2
        var current = minCost(drain)
        while (last != source) {
          val (r, _) = prevEdge(last)
          val e = edges(r.to)(r.pair)
          edges(last)(e.pair) = r.copy(capacity = r.capacity + bottleNeck)
          edges(r.to)(r.pair) = e.copy(capacity = e.capacity - bottleNeck)
          last = r.to
        }
        if (bottleNeck > restFlow) {
          result = m.plus(result, m.times(current, restFlow))
          canMatch = false
        } else {
          restFlow -= bottleNeck
          result = m.plus(result, m.times(current, bottleNeck))
        }
        for (x ← 0 until size if x != source && x != drain){
          potential(x) = m.plus(potential(x), minCost(x))
        }
      }
    }
    result
  }
  private def makeGraph[N](size: Int, edge: Array[(Int, Int, Int, N)])(implicit m: Group[N]): Array[Array[Edge[N]]] = {
    val nodes = Array.tabulate(size){_ ⇒ ArrayBuffer[Edge[N]]()}
    for ((from, to, capacity, cost) ← edge){
      nodes(from).append(Edge(to, capacity, nodes(to).length, cost))
      nodes(to).append(Edge(from, 0, nodes(from).length - 1, m.minus(m.zero, cost)))
    }
    nodes.map{_.toArray}
  }
}