F# での出力

Codeforces 22 Problem C を F# で書いたら TLE 食らいまくったので対処法メモ。

この問題は出力が最大で 10^5 行 (800KB) くらいになるので,出力のところで気をつけないと時間が足りない模様。最初は一行ごとに printfn してたのですが,それだと苦しいようなので StringBuilder を使ってみました。

81190 Jun 30, 2010 6:50:25 PM kozima C - System Administrator F# Accepted 270 ms 20512 KB
81187 Jun 30, 2010 6:28:55 PM kozima C - System Administrator F# Time limit exceeded on test 21 2000 ms 6264 KB

上限 2s で TLE していたのが 270 ms になったそうです。思った以上に差が出ました。正確に何が 270 ms だったのかはよくわかりませんけど。

TLE したコード (81187)

let solve n m v =
  for i = 1 to n do if (i <> v) then (stdout.Write(v); stdout.Write(" "); stdout.WriteLine(i))
  let u = if v = 1 then 2 else 1 in
  let rec loop a b c =
    if c = 0 then ()
    else
      let ok = a <> u && b <> u && a <> v && b <> v in
      let c' = if ok then c-1 else c in
        (if ok then printfn "%d %d" a b);
        if b < n then loop a (b+1) c' else loop (a+1) (a+2) c'
  in loop 1 2 (m - n + 1)

let [n; m; v] = stdin.ReadLine().Split([|' '|]) |> Array.toList |> List.map int in
  if m < n-1 || (n < 10000 && 1 + (n-1) * (n-2) / 2 < m) then
    stdout.WriteLine("-1")
  else solve n m v

solve の中の stdout.Write, stdout.WriteLine, printfn でたくさん出力します。(毎回 flush してる?)

どうでもいいけど for の最後に done; を書くの忘れてますね。F# はインデントを見て適当に処理してくれるみたいです。

通ったコード (81190)

もともと出力していたところを StringBuilder への append で置き換えて,最後にまとめて書き出すようにした。

open System.Text

let solve n m v =
  let sb = StringBuilder() in
  let output a b = ignore <| sb.AppendFormat("{0} {1}\n", a, b) in
    for i = 1 to n do if (i <> v) then output i v;
    let u = if v = 1 then 2 else 1 in
    let rec loop a b c =
      if c = 0 then ()
      else
        let ok = a <> u && b <> u && a <> v && b <> v in
        let c' = if ok then c-1 else c in
          (if ok then output a b);
          if b < n then loop a (b+1) c' else loop (a+1) (a+2) c'
    in loop 1 2 (m - n + 1); stdout.Write(sb.ToString())

let [n; m; v] = stdin.ReadLine().Split([|' '|]) |> Array.toList |> List.map int in
  if m < n-1 || (n < 10000 && 1 + (n-1) * (n-2) / 2 < m) then
    stdout.WriteLine("-1")
  else solve n m v

まとめ

  • 出力が多いときは StringBuilder を使うといいかも
  • StringBuilder は System.Text namespace にいる
  • AppendFormat の第一引数は {0}, {1} とかで残りの引数を参照するらしい
  • ToString() で中身を取り出す
  • 今回は使ってないけど Insert とか Replace とかメソッドがたくさんあって,わりといろいろできるらしい