コンテンツへスキップ

【Ruby基礎】AtCoder Beginner Contest 008 B – 投票

  • by

■はじめに

Rubyの基礎的な問題をたくさん解くことで基本的な考え方やメソッドの使い方を定着させたい。
基本的にはAtCoderというプログラミングコンテスト(競技プログラミング)の過去問を使う。(AtCoderは難易度が分かれており、難易度の低いA問題かB問題を解いていく)

(5/23時点の方針)
メソッドの切り分け方や値の受け渡しを練習するために、コード長の短さについては気にせずに書くことにする。

(2022/10/17時点の方針)
しばらくはB問題を小さい番号の方からやっていく。たまにA問題もやるかも。

■問題

●出典

AtCoder Beginner Contest 008のB問題
https://atcoder.jp/contests/abc008/tasks/abc008_2

●問題文

とある組織で、リーダーを選ぶ選挙が行われた。

組織は N 人の構成員で構成されており、各人は最もリーダーにふさわしい人物の名前を書いた。

リーダーは、得票数が最も多い人物が選ばれることになっている。

得票数が最も多い人物の名前を出力せよ。得票数が最も多い人物が複数いる場合は、そのうちどの名前を出力してもよい。

●入力

入力は以下の形式で標準入力から与えられる。

N
S1
S2
:
SN
  • 1 行目には、組織の構成員の人数を表す整数 N(1≦N≦50) が与えられる。
  • 2 行目から N 行では、それぞれの構成員の投票内容を表す。N 行のうち i(1≦i≦N) 行目には文字列 Si が書かれている。Si は i 番目の人の投票内容を表している。Si は小文字の半角英字のみで構成されており、長さは 1 文字以上 50 文字以下である。

●出力

得票数が最も多い人物の名前を出力せよ。得票数が最も多い人物が複数いる場合は、そのうちどの名前を出力してもよい。出力の末尾にも改行を入れること。

■回答

●愚直に書く

標準入力のパターンとしては何回か前にやったのと同じで、1行目で2行目以降いくつの名前が並ぶのかが入り、2行目以降で実際の名前が並ぶ。

まずは1行目で取得した整数でtimesしてmapして名前を配列で取得する。

次に、どの名前が何回出てくるのかを知りたい。調べていたらtallyメソッドというドンピシャで便利そうなものがあった。配列の要素を数え上げて、何が何回出てくるのかをHashで返してくれる。

数えた結果のHashをsort_byで小さい順に並べると、一番最後の要素が求めたい結果になる。sort_byでHashから配列に変わっているので注意。

n = gets.to_i
s = n.times.map do |x|
  x = gets.chomp
end
puts s.tally.sort_by {|key, value| value}[-1][0]

通った!

●メソッド化して書く

メソッドを作る練習のために、あえてそういう書き方をする。

今回は割愛。

●リファクタリング/別アプローチ

invertというメソッドを使うとhashのキーと値が入れ替わるので、sort_byで値の並び替えをするのではなくsortでキーの並び替えをすれば良いのでちょっとだけシンプルになる、かも…?

n = gets.to_i
s = n.times.map do |x|
  x = gets.chomp
end
puts s.tally.invert.sort[-1][-1]

通った!
ただ、わかりやすさで言えばsort_byのが良いか…。

●他の方の回答例

max_byを使っている方が多いような気がした。
調べたら色々と記事がヒットしたので、配列内で要素数が最も多いものを求める方法としてメジャーなのかもしれない。
自分の回答に適用すると下記のようになる。

n = gets.to_i
s = n.times.map do |x|
  x = gets.chomp
end
puts s.max_by {|a| s.count(a)}

●出てきたメソッド等

公式リファレンスを見る訓練。

■振り返りなど

Hashはまだ経験値が全然足りてないので、使うことができて良かった。
メソッドが色々と出てきてちょっと混乱した。覚えることたくさんあるなぁ。