1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

F#でAtCoder HHKB2020(Cまで)

Posted at

解法に関する解説は公式がやっているので、F#での実装や.NETに関連したことについて自分のわかる範囲で。

A - Keyboard

A - Keyboard

let s = stdin.ReadLine()
let t = stdin.ReadLine()
match s with
| "Y" -> t.ToUpper()
| _ -> t
|> stdout.WriteLine

.NETのライブラリにToUpper()という小文字を大文字に変えてくれるメソッドがあるのでそれを使えばOK。
String.ToUpper メソッド (System) | Microsoft Docs
パターンマッチを使っていますが、別にif..then..elseでももちろんOK。

B - Futon

B - Futon

何故か問題の意味が理解できなくてかなり時間がかかってしまった。

let [|H;W|] = stdin.ReadLine().Split() |> Array.map int
let S = [|for i=0 to H-1 do stdin.ReadLine()|]
let mutable ans = 0
 
for i=0 to H-1 do
    for j=0 to W-2 do
        let s = S.[i].[j..j+1]
        if not (s |> String.exists(fun x -> x='#')) then ans <- ans + 1
 
for i=0 to H-2 do
    for j=0 to W-1 do
        let s = S.[i].[j].ToString() + S.[i+1].[j].ToString()
        if not (s |> String.exists(fun x -> x='#')) then ans <- ans + 1
 
ans |>
stdout.WriteLine

実際、もう少し綺麗に書けそうな気もしますが……。
あとFSharp.Collections.Listを使うとインデックスを用いたアクセスが遅くなるので配列の方が良い。

C - Neq Min

C - Neq Min

最初、「この書き方だとTLEになるよね」と思いつつTLEになるパターンで出しました😂

let N = stdin.ReadLine() |> int
let p = stdin.ReadLine().Split() |> Array.map int
let memo : int[] = Array.zeroCreate 200001
let mutable minNum = 0
for i=0 to N-1 do
    let rec f n =
        if memo.[n] = 0 then n
        else f (n+1)
    memo.[p.[i]] <- memo.[p.[i]]+1
    minNum <- if p.[i] = minNum then (f minNum) else minNum
    minNum |> printfn "%d"

例のごとく、Listではなく配列でないとインデックスを用いた要素のアクセスが遅いので、どの整数が出現したかどうか記録するコレクションは配列を用います。

C#で競技プログラミングをし続ける人のためのTipsにもあるように、C#ではConsole.WriteLineが低速であるのと同様に、F#のprintfnも内部でConsole.Outを使っているので遅いです。
このままだと800msぐらいかかるので、文字列の連結やSystem.Text.StringBuilderクラスを使う、AutoFlushのプロパティをOFFにした独自の出力方法を使うなど、何かしら工夫をして最後に一気に出力する方が本当は良いです。

いい加減AtCoder用のテンプレートプロジェクトを作っておく必要性を感じています。

open System.Collections.Generic
let N = stdin.ReadLine() |> int
let mutable ans = 0
let s = new HashSet<int>()  //let mutable s = Set<int> [||]
let sb = System.Text.StringBuilder()
 
let rec g n =
    if s.Contains(n) |> not then n
    else g (n+1)
 
for p in stdin.ReadLine().Split() do
    if s.Add(p |> int) then ans <- g ans // s <- p |> int |> s.Add
    sb.AppendLine(ans.ToString()) |> ignore
 
sb.ToString() |> stdout.Write

終了後、他のC#の回答を見てHashSetなどを使ってみた場合はこちら。再帰関数をwhile..doに変えることもできますが、再帰の方が誤差かもしれないが速い。あとforループの外で関数定義した方が数十ms程度かもしれませんが速い。

ちなみに、コメントで示したようにF#のSetmutableにして使ったり、再帰を利用して使ったりしても遅いのでおとなしくSystem.Collections.Generic.HashSetを使った方がよさげです。

おわりに

最近本腰を入れて始めたばかりですが、F#での参加者は非常に少ないのでこれからも何かしらの何かを書いていきたいところです。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?