コンテンツへスキップ

【Ruby基礎】AtCoder Beginner Contest 001 B – 視程の通報

  • by

INDEX

■はじめに

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

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

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

■問題

●出典

AtCoder Beginner Contest 001のB問題
https://atcoder.jp/contests/abc001/tasks/abc001_2

●問題文

気象情報は、世界中に様々な形で流れています。そのひとつの地上実況気象通報式 (SYNOP) では、視程 (肉眼で物体がはっきりと確認できる最大の距離) を、次の規則に従って、VVという値 (通報式) に変換して報じます。

  1. 0.1km 未満: VVの値は 00 とする。
  2. 0.1km 以上 5km 以下:距離 (km) を 10 倍した値とする。1 桁の場合は上位に 0 を付す。
    • 例えば、2,000m =2.0km ならば、VVは 20 である。同じく、200mの場合VVは 02 である。
  3. 6km 以上 30km 以下:距離 (km) に 50 を足した値とする。
    • 例えば、15,000m =15km ならば、VVは 65 である。
  4. 35km 以上 70km 以下:距離 (km) から 30 を引いて 5 で割った後、80 を足した値とする。
    • 例えば、40,000m =40km ならば、VVは 82 である。
  5. 70km より大きい:VVの値は 89 とする。

いま、あなたに視程の距離をメートルで与えるので、上記のルールに従って計算されるVVを出力するプログラムを作成してください。

なお、VVは必ず(上位の 0 を含めて)2桁の整数であり、上記のルールに従って計算した時に整数にならないような入力や、上記の範囲に入らない入力 (例:5km より大きく 6km 未満) などはありません。

●入力

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

m
  • 1 行目には、距離を表す整数 m (0≦m≦100,000) が与えられる。単位はメートル (m) である。

●出力

VVの値を 1 行で出力せよ。
また、出力の末尾には改行を入れること。

■回答

●愚直に書く

ひたすら条件分岐していくか。

m = gets.to_f / 1000
if m < 0.1
  puts format("%02d", 0)
elsif m >= 0.1 && m <= 5
  puts format("%02d", m * 10)
elsif m >= 6 && m <= 30
  puts format("%02d", m + 50)
elsif m >= 35 && m <= 70
  puts format("%02d", (m - 30) / 5 + 80)
elsif m > 70
  puts 89
end

通った!
to_fformatを使った。

あとは、elsifを並べるよりcase whenを使う方が良いか。

m = gets.to_f / 1000

case m
  when 0...0.1
    puts format("%02d", 0)
  when 0.1..5
    puts format("%02d", m * 10)
  when 6..30
    puts format("%02d", m + 50)
  when 35..70
    puts format("%02d", (m - 30) / 5 + 80)
  when 70.001..100
    puts 89
end

通った!最後のwhen 70.001..100があまり綺麗じゃない気がするが…。

●メソッド化して書く

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

def main
  m = read_num
  puts judge_vv(m)
end

def judge_vv(m)
  case m
    when 0...0.1
      format("%02d", 0)
    when 0.1..5
      format("%02d", m * 10)
    when 6..30
      format("%02d", m + 50)
    when 35..70
      format("%02d", (m - 30) / 5 + 80)
    when 70.001..100
      89
  end
end

def read_num
  gets.to_f / 1000
end

main

通った!
ただ、format("%02d"の重複が気になるなぁ…
"%02d"を変数に入れることはすぐできそう。

def main
  m = read_num
  puts judge_vv(m)
end

def judge_vv(m)
  fill = "%02d"
  case m
    when 0...0.1
      format(fill, 0)
    when 0.1..5
      format(fill, m * 10)
    when 6..30
      format(fill, m + 50)
    when 35..70
      format(fill, (m - 30) / 5 + 80)
    when 70.001..100
      89
  end
end

def read_num
  gets.to_f / 1000
end

main

通った!
もう少しできそうだけど時間切れのためここまで…。

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

上で色々とやっているので、ここでは割愛。

●他の方の回答例

最上位の方達の回答が全然わからない…涙。
少し下の方に行って、一旦計算結果を別の変数に入れてformatは最後に1回だけ行う、というのが参考になった。
自分の回答に適用すると下記のようになる。

m = gets.to_f / 1000

case m
  when 0...0.1
    v = 0
  when 0.1..5
    v = m * 10
  when 6..30
    v = m + 50
  when 35..70
    v =  (m - 30) / 5 + 80
  when 70.001..100
    v = 89
end

puts format("%02d", v)

なるほど!
ということはメソッド化した方でも同じようにできそう。

def main
  m = read_num
  puts format("%02d", judge_vv(m))
end

def judge_vv(m)
  case m
    when 0...0.1
      0
    when 0.1..5
      m * 10
    when 6..30
      m + 50
    when 35..70
      (m - 30) / 5 + 80
    when 70.001..100
      89
  end
end

def read_num
  gets.to_f / 1000
end

main

通った!結構見通しが良くなったような気がする。

●出てきたメソッド等

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

■振り返りなど

B問題はやっぱりちょっと難しくなる。愚直に書けと言われればなんとか回答できるけど、できるだけ綺麗な回答に辿り着けるようになりたい。