LoginSignup
2
2

More than 5 years have passed since last update.

Codeforces #257 div.2 A

Last updated at Posted at 2014-07-25

概要

http://codeforces.com/contest/450/problem/A
をScalaで解いてみた。
競プロのリハビリ兼Scalaが競プロでどう使えるのかの検証その1です。

問題

n人の子供が並んでいる(1..nの番号付き)。
それぞれ、少なくともa[i]個のキャンディを受け取るまで帰らない。
Jzzhuさんは先頭の子からm個ずつキャンディを与える。
m個もらった子iは、もらった数の合計がa[i]を上回れば列から消え、まだなら列の最後に並ぶ。
最後に列に残った子は何番?

1 \leq n \leq 100

アルゴリズム

子供の列をListで表し、実装するだけ。
nが小さいので何しても大丈夫。
良心的なA問題。

Scala的には

列の最後に追加、というList的に不吉な文言が。
念のため、末尾に追加演算子(:+)を使わずreverseしてみた。
両方コドフォサーバーで試したところ、実行速度に差はほぼなし。

100程度なら何やっても大丈夫そう。
詳しい検証は速度が厳しい問題が出てからにしよう…。

こういうケース、うまく書けば再帰使わないで行けるんかな…?
末尾に増える時点でmapじゃ書けなさそうだし、あんまりいい方法が浮かばない。

なお、テストをしやすいように、solve関数にまとめた。

解答

import scala.annotation.tailrec

object Main extends App {
  val sc = new java.util.Scanner(System.in)
  val n,m = sc.nextInt
  val input = List.fill(n)(sc.nextInt)

  def solve( m: Int, input: List[Int] ): Int = {

    @tailrec
    def dist(children: List[(Int, Int)] ): Int = {
      children match {

        case List(x) =>  // size == 1
          x._2 + 1

        case x :: xs =>
          val diff = x._1 - m
          if (diff <= 0)
            dist(xs)
          else
            dist( ( (diff, x._2) :: xs.reverse ).reverse )
      }
    }

    dist( input.zipWithIndex )
  }

  println( solve( m, input ) )
}

自分で書いたテストケース

import org.scalatest._

class Test extends FlatSpec with Matchers {

  it should "test 1" in {
    val input = List(1, 3, 1, 4, 2)
    Main.solve(2, input) should be (4)
  }

  it should "test 2" in {
    val input = List(1, 1, 2, 2, 3, 3)
    Main.solve(4, input) should be (6)
  }

  it should "test 3" in {
    val input = List(6, 1, 5, 4, 2, 3, 1)
    Main.solve(3, input) should be (4)
  }
}

結果

Time: 249 ms
Memory: 100 KB

Scala見たことない人へのちょっとした解説

  • List[Int]
    IntのList。いわゆる単方向リスト。
    特定の要素に行くには先頭から辿らないと行けないので、末尾に追加とかは厄介。
    その代わり、先頭に追加、とか先頭を順に取り出し、とかは速い。

  • List[(Int,Int)]
    Intを2つ持つタプルのList。タプルを書くのは簡単。便利。

  • @tailrec
    末尾再帰をしてね、ってお願いするアノテーション。
    末尾再帰な関数は、whileに展開されてスタックを食いつぶさなくなる、らしい。
    心休まるアノテーション。

  • match - case
    C言語のそれと比べてはいけないレベルで強力。
    数字、文字列のみならず、型やリストでもマッチでき、ガード条件も付けられる。

  • ;
    省略可能。1行に処理を並べたい時は必要。

  • return
    省略可能。ブロックの最後に書いたのがreturn値になる。便利。
    ブロックを使って変数を複雑な初期化とかすると綺麗&&便利。

  val t = {
    val u = a+b
    u*v
  }
  • reverse 要素の順番をひっくり返した配列を返す。 「reverse」→「先頭に要素追加」→「reverse」であら不思議、末尾に要素が追加できちゃう。 Listの末尾に追加するコストを気にしなければ、 List() :+ elem で末尾に追加できる演算子が用意されている。
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2