はじめに
n番煎じですが、@drkenさんの記事 AtCoder に登録したら次にやること ~ これだけ解けば十分闘える!過去問精選 10 問 ~に取り上げられている問題10問を Ruby で解いてみました。
解法の詳細な解説は元記事をご参照ください。
第 1 問: ABC 086 A - Product (100 点)
a, b = gets.strip.split.map(&:to_i)
puts (a * b).odd? ? 'Odd' : 'Even'
一行に複数の数値が入力されるときにそれらを受け取るお決まりのパターンです。また、Ruby には偶数奇数を判定する even?
や odd?
という組み込みのメソッドがあるのでそれを活用しています。
第 2 問: ABC 081 A - Placing Marbles (100 点)
nums = gets.strip.split('').map(&:to_i)
puts nums.inject(:+)
配列の和を計算するには、Ruby2.4以降では nums.sum
でOKですが、ここで判定に用いられているのは 2.3.3 なので従来の inject
メソッドを使っています。
第 3 問: ABC 081 B - Shift Only (200 点)
_n = gets.to_i
as = gets.strip.split.map(&:to_i)
cnt = 0
while as.all?(&:even?) do
cnt = cnt.succ
as = as.map { |a| a / 2 }
end
puts cnt
「すべてが偶数である」という条件の判定に all?
という便利メソッドを使っています。また、 Ruby には++
のようなインクリメント演算子はなく、cnt += 1
のような記法や cnt.succ
, cnt.next
のようなメソッドを用います。
第 4 問: ABC 087 B - Coins (200 点)
a, b, c, x = 4.times.map { gets.to_i }
cnt = 0
(0..a).each do |i|
(0..b).each do |j|
(0..c).each do |k|
cnt += 1 if i * 500 + j * 100 + k * 50 == x
end
end
end
puts cnt
後置 if
記法 cnt += 1 if i * 500 + j * 100 + k * 50 == x
を使っています。
第 5 問: ABC 083 B - Some Sums (200 点)
n, a, b = gets.strip.split.map(&:to_i)
# 各桁の和を求める
def digit_sum n
sum = 0
while n > 0 do
sum += n % 10
n /= 10
end
sum
end
ans = (1..n)
.map { |i| [i, digit_sum(i)] }
.select { |d| a <= d[1] && d[1] <= b }
.inject(0) { |sum, d| sum + d[0] }
puts ans
各桁の和を求める関数は標準的な書き方ですね。
後半では、map
, select
, inject
のような配列操作関数を活用しています。
第 6 問: ABC 088 B - Card Game for Two (200 点)
_n = gets.to_i
as = gets.strip.split.map(&:to_i).sort.reverse
alice = 0
bob = 0
until as.empty? do
alice += as.shift
break if as.empty?
bob += as.shift
end
puts (alice - bob)
ソートや逆順に並べ替える等の操作が非常に手軽にできるところは Ruby の利点ですね。
他に、 while !条件
と等価な until
記法を用いています。
第 7 問: ABC 085 B - Kagami Mochi (200 点)
n = gets.to_i
mochis = n.times.map { gets.to_i }
puts mochis.uniq.size
配列から重複を取り除く uniq
メソッドを用いて簡単に解くことができました。
第 8 問: ABC 085 C - Otoshidama (300 点)
n, y = gets.strip.split.map(&:to_i)
_i, _j, _k = -1, -1, -1
(0..n).each do |i|
(0..n-i).each do |j|
if i * 10000 + j * 5000 + (n - i - j) * 1000 == y
_i, _j, _k = i, j, n - i - j
end
end
end
puts "#{_i} #{_j} #{_k}"
結果の出力で、文字列の中に変数の値を埋め込む「変数展開」を活用しています。
第 9 問: ABC 049 C - Daydream (300 点)
s = gets.strip
words = ['dream', 'dreamer', 'erase', 'eraser']
until s.empty? do
reduced = false
words.map do |w|
if s[-w.size, w.size] == w
s = s[0..-w.size - 1]
reduced = true
break
end
end
unless reduced
puts 'NO'
exit 0
end
end
puts 'YES'
部分文字列を切り出す操作を []
を用いて直感的にできるのは利点ですね。
第 10 問: ABC 086 C - Traveling (300 点)
n = gets.to_i
data = n.times.map { gets.strip.split.map(&:to_i) }
def is_reachable(current, next_data)
# 次の地点までのマンハッタン距離
tc, xc, yc = current
tn, xn, yn = next_data
dist = (xc - xn).abs + (yc - yn).abs
# 最短距離でも間に合わない
return false if dist > tn - tc
# 行ったり来たりでちょうど到着できるか
(dist - (tn - tc)).even?
end
current = [0, 0, 0]
data.each do |d|
unless is_reachable(current, d)
puts 'No'
exit 0
end
current = d
end
puts 'Yes'
他言語でもわりと見かけますが、最終行にメソッドの返り値を書く場合は return
を省略できます。