Problem:
シルクロード (Silk Road)

問題

現在カザフスタンがある地域には，古くは「シルクロード」と呼ばれる交易路があった．

シルクロード上には N + 1 個の都市があり，西から順に都市 0, 都市 1, ... , 都市 N と番号がつけられている．都市 i - 1 と都市 i の間の距離 (1 ≤ i ≤ N) は Di である．

貿易商である JOI 君は，都市 0 から出発して，都市を順番に経由し，都市 N まで絹を運ぶことになった．都市 0 から都市 N まで M 日以内に移動しなければならない．JOI 君は，それぞれの日の行動として，以下の 2 つのうちいずれか 1 つを選ぶ．

移動： 現在の都市から 1 つ東の都市へ 1 日かけて移動する．現在都市 i - 1 (1 ≤ i ≤ N) にいる場合は，都市 i に移動する．

待機： 移動を行わず，現在いる都市で 1 日待機する．

移動は大変であり，移動するたびに疲労度が溜まっていく．シルクロードでは日毎に天候の変動があり，天候が悪い日ほど移動には苦労を要する．

JOI 君が絹を運ぶのに使える M 日間のうち j 日目 (1 ≤ j ≤ M) の天候の悪さは Cj であることが分かっている．都市 i - 1 から都市 i (1 ≤ i ≤ N) に j 日目 (1 ≤ j ≤ M) に移動する場合，疲労度が Di × Cj だけ溜まってしまう．移動を行わず待機している日は疲労度は溜まらない．

JOI 君は，それぞれの日の行動をうまく選ぶことで，できるだけ疲労度を溜めずに移動したい．JOI 君が M 日以内に都市 N に移動するときの，移動を開始してから終了するまでに溜まる疲労度の合計の最小値を求めよ．

入力

入力は 1 + N + M 行からなる．

1 行目には，2 つの整数 N, M (1 ≤ N ≤ M ≤ 1000) が空白を区切りとして書かれている．これは，シルクロードが N + 1 個の都市からなり，JOI 君が絹を都市 0 から都市 N まで M 日以内に運ばなければならないことを表す．

続く N 行のうちの i 行目 (1 ≤ i ≤ N) には，整数 Di (1 ≤ Di ≤ 1000) が書かれている．これは，都市 i - 1 と都市 i の間の距離が Di であることを表す．

続く M 行のうちの j 行目 (1 ≤ j ≤ M) には，整数 Cj (1 ≤ Cj ≤ 1000) が書かれている．これは，j 日目の天候の悪さが Cj であることを表す．

出力

JOI 君が M 日以内に都市 N に移動するときの，移動を開始してから終了するまでに溜まる疲労度の合計の最小値を 1 行で出力せよ．

入出力例

入力例 1

3 5
10
25
15
50
30
15
40
30

出力例 1

1125

入力例 2

2 6
99
20
490
612
515
131
931
1000

出力例 2

31589

入出力例 1 において，溜まる疲労度の合計を最小にするように JOI 君が移動するには次のようにする．

1 日目は待機する．

2 日目に都市 0 から都市 1 へ移動する．このとき溜まる疲労度は 10 × 30 = 300 である．

3 日目に都市 1 から都市 2 へ移動する．このとき溜まる疲労度は 25 × 15 = 375 である．

4 日目は待機する．

5 日目に都市 2 から都市 3 へ移動する．このとき溜まる疲労度は 15 × 30 = 450 である．

JOI 君がこのように移動した場合に溜まる疲労度の合計は 300 + 375 + 450 = 1125 である．これが最小値である．

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in Scala (judge verdict: Accepted)

import scala.annotation.tailrec
import net.pushl.io.MyConsole

package net.pushl {
  package number {
    // Prime    (Prime.scala)
    // Number   (Number.scala)
    // Combi    (Combi.scala)
    // Optimize (Optimize.scala)
  }
  package string {
    // RollingHash (RollingHash.scala)
  }
  package geometry {
    // Point   (Geometry.scala)
    // Segment (Geometry.scala)
    // Line    (Geometry.scala)
  }
  package datastructure {
    // UnionFindTree (UnionFind.scala)
  }
  package io {
  }
}

// Document: http://www.scala-lang.org/api/current/#package
// -----------------------------------------------------------------------------
class Solver(val stdio: MyConsole){
  import stdio._ // shadow Console.~
  def solve(mincost: Vector[Int], dists: Vector[Int], weather: List[Int]) : Int =
    weather match {
      case Nil    => mincost.last
      case h :: t => solve((mincost.zipWithIndex).map({case (c,i) => {
                                                         if(i >= 1)
                                                           c min (mincost(i-1) + h * dists(i-1))
                                                         else c
                                                       }}),
                           dists, t)
    }
  def main() : Unit = {
    val INF = 1000000000
    val n,m = nextInt
    val d   = (1 to n).map(_ => nextInt).toVector
    val c   = (1 to m).map(_ => nextInt).toList
    println(solve((Vector.fill(n+1)(INF).updated(0,0)),d,c))
  }
}









// ----------------------------------------------------------------------------0
// ----------------------------------------------------------------------------0
object Main {
  def main(args: Array[String]) : Unit = {
    val console = new MyConsole(Console.in, Console.out, Console.err)
    val solver  = new Solver(console)
    solver.main()
    console.flush()
  }
}
package net.pushl.io {
  import java.io.{BufferedReader, PrintWriter, PrintStream, PushbackReader}
  class MyConsole(val in: BufferedReader, val _out: PrintStream,
                  val err: PrintStream) {
    // PrintWriter do not flush automatically
    val out = new PrintWriter(_out,false)
    // If argument is null, there are ambiguous which function will be called
    def print(obj: Any)                  = out.print(if(obj == null) "null" else obj.toString)
    def println()                        = out.println()
    def println(obj: Any)                = out.println(obj)
    def printf(text: String, args: Any*) = out.printf(text.format(args : _*))
    // NOTE: YOU MUST FLUSH BEFORE END OF MAIN
    def flush()                          = out.flush()
    def debugln(obj: Any)                = err.println(obj)

    def readIntVector()    : Vector[Int]    = parseLineToVector(() => nextInt)
    def readLongVector()   : Vector[Long]   = parseLineToVector(() => nextLong)
    def readStringVector() : Vector[String] = parseLineToVector(() => nextString)
    def nextInt()          : Int            = nextLong().toInt
    def nextBigInt()       : BigInt         = BigInt(nextString())
    def nextBigDecimal(  ) : BigDecimal     = BigDecimal(nextString())
    def nextDouble()       : Double         = nextString().toDouble
    def nextLong() : Long = {
      if(!goNextValuable())
        throw new NoSuchElementException("Reading long failed")
      val sgn = if(peek == '-') -1l else 1
      if(sgn == -1l) read()
      if(peek < '0' || '9' < peek)
        throw new NumberFormatException(s"readLong found only '-' or no number")
      @tailrec
      def readLong(next: Int, cur: Long) : Long =
        if('0' <= next && next <= '9')
          readLong(readWithoutCheckingPeeked(), cur*10 + next-'0')
        else if(isEnd(next) || isSpaceOrControl(next))
          sgn*cur
        else
          throw new NumberFormatException(s"readLong found strange byte $next")
      val res = readLong(read(),0)
      skipTrailingSpaces()
      res
    }
    def nextString() : String = {
      if(!goNextValuable())
        throw new NoSuchElementException("Reading String failed")
      val builder = new StringBuilder
      @tailrec
      def appendCode(next: Int) : String = {
        if(isEnd(next) || isSpaceOrControl(next)){
          builder.toString
        }else{
          builder.append(next.toChar)
          appendCode(readWithoutCheckingPeeked())
        }
      }
      val res = appendCode(read())
      skipTrailingSpaces()
      res
    }
    // TODO: refactoring to marge nextString
    def readLine() : String = {
      if(isEnd(peek))
        throw new NoSuchElementException("Reading Line failed")
      val builder = new StringBuilder
      @tailrec
      def appendCode(next: Int) : String = {
        if(isEnd(next) || isNewLine(next)){
          builder.toString
        }else{
          builder.append(next.toChar)
          appendCode(read())
        }
      }
      appendCode(read())
    }
    // helpers
    private[this] var peeked: Option[Int] = None
    private[this] var last = -1
    private def read() = {
      val res = peeked match {
          case None    => in.read()
          case Some(a) => { peeked = None; a }
        }
      last = res
      res
    }
    @inline private def readWithoutCheckingPeeked() = {
      val res = in.read()
      last = res
      res
    }
    @inline private def peek() =
      peeked match {
        case None    => {
          val res = in.read()
          peeked  = Some(res)
          res
        }
        case Some(a) => a
      }
    @inline private def isEnd(c: Int)    = c == -1
    @inline private def isNewLine(c: Int) = c == 10 || c == 13     // LF and CR
    @inline private def isThereReadable() = !isEnd(peek)
    // XXX: this limits c is ASCII?
    @inline private def isSpaceOrControl(c: Int) = (0 <= c && c <= 32) || c == 127
    @tailrec
    final private def goNextNotSpaceNorControl() : Unit =
      if(isSpaceOrControl(peek)){
        read()
        goNextNotSpaceNorControl()
      }
    final private def skipTrailingSpaces() : Unit = {
      @tailrec
      def skipTrailingSpacesAux() : Unit = {
        if(!isNewLine(last) && !isNewLine(peek) && isSpaceOrControl(peek)){
          read()
          skipTrailingSpacesAux()
        }
      }
      skipTrailingSpacesAux
      if(!isNewLine(last) && isNewLine(peek)) read()
    }
    @tailrec
    final private def skipTrailingSpacesAndNewline() : Unit =
      if(isNewLine(peek)){
        val _ = read() // windows causes error. maybe.
      }else if(isSpaceOrControl(peek)){
        read()
        skipTrailingSpacesAndNewline()
      }
    @inline private def goNextValuable() = {
      goNextNotSpaceNorControl()
      isThereReadable()
    }
    @inline private def parseLineToVector[X](parser: () => X) : Vector[X] = {
      import scala.collection.immutable.VectorBuilder
      val vb = new VectorBuilder[X]()
      @tailrec
      def parseLineToVectorAux(first: Boolean=false) : Vector[X] =
        if((!first && isNewLine(last)) || isNewLine(peek) || isEnd(peek)) {
          vb.result
        }else{
          vb += parser()
          parseLineToVectorAux()
        }
      parseLineToVectorAux(true)
    }
  }
}