コンテンツへスキップ

【Ruby基礎】AtCoder Beginner Contest 031 B – 運動管理

INDEX

■はじめに

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

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

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

■問題

●出典

AtCoder Beginner Contest 031のB問題
https://atcoder.jp/contests/abc031/tasks/abc031_b

●問題文

高橋君は AtCoder 社のマスコットキャラクターである。

マスコットキャラクターとしての魅力を維持するために、高橋君は適度な運動をすることになっている。高橋君は週の運動時間が L 分以上 H 分以下でなければならない。

しかしながら、青木君は最近、高橋君が運動しているところを見ていない。高橋君の運動状況が気になった青木君は、高橋君の過去
N 週間の運動時間が制限にあっているのか、そして足りないなら少なくともあと何分運動する必要があったのかを計算するプログラムを作成することにした。

●制約

  • ああああ

●入力

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

L H
N
A1
A2
:
AN
  • 1 行目には、2 個の整数 L, H(1≦L≦H≦10^4 ) が空白区切りで与えられる。これは、高橋君が週に L 分以上
    H 分以下の運動をしなければならないことを表す。
  • 2 行目には、整数 N(1≦N≦50) が与えられる。これは青木君が N 週分の運動時間について調べていることを表す。
  • 3 行目からの N 行には、高橋君の運動状況を表す整数が与えられる。N 行の内 i(1≦i≦N) 行目には、整数 Ai (0≦Ai ≦10^4 ) が与えられる。これは i 個前の週において高橋君が Ai 分運動したことを表す。

●出力

出力は N 行からなる。i(1≦i≦N) 行目には、i 個前の週において高橋君が必要な分よりも多く運動している場合は −1 を、そうでない場合は追加で必要な運動時間の最小値を分単位で 1 行に出力せよ。
出力の末尾にも改行を入れること。

■回答

●愚直に書く

久しぶりのB問題。
まずは標準入力をちゃんと取るところから。

l, h = gets.split.map(&:to_i)
n = gets.to_i
a = n.times.map do |line|
  line = gets.to_i
end

# 以下は確認用
p l, h
p n
p a

これで標準入力は取れた。

次に、配列aの各要素がl以上h以下かどうかを判定していく感じか。

l, h = gets.split.map(&:to_i)
n = gets.to_i
a = n.times.map do |line|
  line = gets.to_i
end

a.each do |elm|
  if elm < l
    puts l - elm
  elsif elm > h
    puts -1
  else
    puts 0
  end
end

通った!

●メソッド化して書く

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

メインメソッド、判定用メソッド、標準入力を取るメソッド2つの計4つ。
メインメソッド内のeachを回す部分をさらに別メソッドに分けても良いかな?

def main
  l, h = read_target
  a = read_record
  a.each do |elm|
    puts judge(l, h, elm)
  end
end

def judge(l, h, elm)
  if elm < l
    l - elm
  elsif elm > h
    -1
  else
    0
  end
end

def read_target
  gets.split.map(&:to_i)
end

def read_record
  n = gets.to_i
  n.times.map do |line|
    line = gets.to_i
  end
end

main

通った!

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

色々試したけど上手く整理できなかったので割愛…!

●他の方の回答例

maxを使った解答があり、なるほどと思った。

自分の回答に取り入れると以下のような感じ。

l, h = gets.split.map(&:to_i)
n = gets.to_i
a = n.times.map do |line|
  line = gets.to_i
end

a.each do |elm|
  if elm <= h
    puts [l - elm, 0].max
  else
    puts -1
  end
end

これだとif内がシンプルになるしそれによって三項演算子にもしやすい。なるほど。

●出てきたメソッド等

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

■振り返りなど

計算するのにちょっと混乱してしまった。
とはいえB問題を愚直にでも解けたので良かった。