競技プログラミングのためのF#入門
GitHub上の対応ディレクトリ 公式ページ, ABC 077 C - Snuke Festival 公式解説, PDF 解説
公式解説通り入力Aa
をシフトした配列をBa
とします. 解説の便宜のためBa
をソートした配列をCa
としましょう.
公式解説通りに実装すれば問題ありません. 他の人のコードを見ていると中央値の扱いにぶれがあるようです. 単にBa.[N/2]
だけとしているコードもあれば, 偶奇でわけたコードもあります.
ちなみに私はさらに違う処理にしました. 選ぶべきb
が整数でなければならないため, N
が奇数のときはCa.[N/2]
, N
が偶数のときはCa.[N/2]
とCa.{N/2-1}
を中央値の候補としています. もちろんN
が偶数のときは二つの値を計算して小さい方を取ります.
どう書くとすっきりするかは人によるものの, 私は中央値の候補を配列にして処理しました. 具体的には次のように中央値の候補を作っています.
| let Ba = Aa |> Array.mapi (fun i a -> a - (int64 (i+1)))
let Ma = Ba |> Array.sort |> fun Ba -> if N%2=1 then [|Ba.[N/2]|] else [|Ba.[N/2-1];Ba.[N/2]|]
|
最終的な悲しさの最小値は中央値の配列Ma
から次のように計算しています.
| Ma |> Array.map (fun b -> Ba |> Array.sumBy (fun x -> abs(x-b))) |> Array.min
|
よほど複雑な処理をかませる場合は適切な対処が必要ですが, Array.map f |> Array.sum
はArray.sumBy
で書くとすっきりします.
私がはまり倒したため念のため書いておきます. 次のように書くとMa
の値を取ってしまい正しい悲しさの最小値が得られません.
| Ma |> Array.minBy (fun b -> Ba |> Array.sumBy (fun x -> abs(x-b)))
|
おまけ
次の二つのコードの結果が面白いです.
| let n = int (stdin.ReadLine())
let a = stdin.ReadLine().Split() |> Array.mapi (fun i x -> int64 x - int64 i - 1L) |> Array.sort
let b = if n % 2 = 0 then (a.[n / 2] + a.[n / 2 - 1]) / 2L else a.[n / 2]
printfn "%d" (Array.sumBy (fun x -> abs (x - b)) a)
|
| let n = int (stdin.ReadLine())
let a = stdin.ReadLine().Split() |> Array.mapi (fun i x -> int64 x - int64 i + 1L) |> Array.sort
let b = if n % 2 = 0 then (a.[n / 2] + a.[n / 2 - 1]) / 2L else a.[n / 2]
printfn "%d" (Array.fold (fun (l : int64) k -> l + abs (k - b)) 0L a)
|
違うのは最後の和を取る部分でsumBy
なのかfold
かです. 実行時間は変わらないものの消費メモリが一割違います. 少なくともMono 4.0
でメモリ効率を考えるならfold
の方がよいようです.
そしてさらに私の提出コードも見てみましょう.
| let solve N Aa =
let Ca = Aa |> Array.mapi (fun i a -> a - (int64 (i+1)))
let Ba = Ca |> Array.sort |> fun Ba -> if N%2=1 then [|Ba.[N/2]|] else [|Ba.[N/2-1];Ba.[N/2]|]
Ba |> Array.map (fun b -> Ca |> Array.sumBy (fun x -> abs(x-b))) |> Array.min
let N = stdin.ReadLine() |> int
let Aa = stdin.ReadLine().Split() |> Array.map int64
solve N Aa |> stdout.WriteLine
|
偶数項のとき和を二回取っているため明らかにこちらの方が遅いはずですが, Mono 4.0
よりも高速です. Mono 4.0のリリースが2015年, .NET core 3.1.201のリリースが2021年で後者の方が新しいため, 処理系の高速化のおかげでしょう.
Previous: 070 C - Snuke Festival Next: 072 C - 4/N back to top