paiza プログラミング

[Ruby]paiza スキルチェック過去問題セット 野球の審判 (paizaランク C 相当)の解説

paiza解説 野球の審判

こんにちは!じゃいごテックのあつしです。

今回はpaizaレベルアップ問題集から野球の審判という問題を解説していきたいと思います。こちらは過去にスキルチェックで実際に出題されていた問題らしいです!

難易度は 1276±10 で、C級としては簡単な問題だと思います。

野球の審判 (paizaランク C 相当) を解いてみる

※ paiza レベルアップ問題集 スキルチェック過去問題セットより

問題

野球の各打者はストライクが 3 つたまるとアウトとなり、ボールが 4 つたまるとフォアボールとなります。
アウトあるいはフォアボールになると、この打者の番は終了します。

あなたはストライクとボールを判定してコールする審判です。
その場の状況に合わせて適切なコールを出しましょう。

【コール一覧】

  • ストライクが 1 〜 2 つたまったとき → "strike!"
  • ストライクが 3 つたまったとき → "out!"
  • ボールが 1 〜 3 つたまったとき → "ball!"
  • ボールが 4 つたまったとき → "fourball!"

コールの例

ある打者の番における投球の結果 (ストライクまたはボール) が与えられるので、各投球に対してどのようなコールをすればよいかを出力してください。
なお、実際の野球にあっても上記にないルール (ヒット、ファウルなど) については考慮する必要はありません。

1 球目: ボール → "ball!"
2 球目: ストライク → "strike!"
3 球目: ボール → "ball!"
4 球目: ストライク → "strike!"
5 球目: ストライク → "out!"

5 球目では、ボールが 2 つ、ストライクが 3 つたまったのでこの打者はアウトとなります。

1 球目: ボール → "ball!"
2 球目: ストライク → "strike!"
3 球目: ボール → "ball!"
4 球目: ボール → "ball!"
5 球目: ストライク → "strike!"
6 球目: ボール → "fourball!"

6 球目では、ストライクが 2 つ、ボールが 4 つたまったのでこの打者はフォアボールとなります。

入力される値

入力は、以下のフォーマットで与えられます。

N
s_1
s_2
...
s_n

1 行目に合計の投球数を表す整数 N が与えられます。
続く N 行のうち i 行目 (1 ≦ i ≦ N) に i 番目の投球の結果を表す文字列 s_i が与えられます。i 番目の投球がストライクであれば s_i は "strike"、ボールであれば "ball" となります。
入力は合計で N + 1 行からなり、入力値最終行の末尾に改行が1つ入ります。

入力値最終行の末尾に改行が1つ入ります。

期待する出力

各投球に対する適切なコールを以下の形で出力してください。

c_1
c_2
...
c_N

期待する出力は N 行からなります。
N 行のうちの i 行目 (1 ≦ i ≦ N) に問題文の条件に即して打者へのコールを表す文字列 c_i を "strike!", "ball!", "out!", "fourball!" のいずれかで出力してください。
出力の N 行目の末尾に改行を 1 つ入れ、余計な文字、空行を含んではいけません。

条件

すべてのテストケースにおいて、以下の条件をみたします。

  • 3 ≦ N ≦ 6
  • s_i (1 ≦ i ≦ N) は英字小文字で構成される文字列で、"strike", "ball" のいずれか。
  • 投球はこの打者の番がちょうど終了するまで続きます。
    すなわち、最終的にこの打者にはアウトかフォアボールのいずれかが必ず与えられ、それ以降の投球はおこなわれません。

入力例1
5
ball
strike
ball
strike
strike
出力例1
ball!
strike!
ball!
strike!
out!

入力例2
6
ball
strike
ball
ball
strike
ball
出力例2
ball!
strike!
ball!
ball!
strike!
fourball!

攻略ポイント
問題を解く流れ

入出力例をコピペしてヒアドキュメントで変数に代入し、データを確認します。INPUT1 が与えられた時に OUTPUT1 を出力すれば正解と言うことになります。
きちんと受け取れていれば確認用コードは削除します。

INPUT1 = <<~"EOS"
  5
  ball
  strike
  ball
  strike
  strike
EOS
OUTPUT1 = <<~"EOS"
  ball!
  strike!
  ball!
  strike!
  out!
EOS

INPUT1 = <<~"EOS"
  6
  ball
  strike
  ball
  ball
  strike
  ball
EOS
OUTPUT1 = <<~"EOS"
  ball!
  strike!
  ball!
  ball!
  strike!
  fourball!
EOS

# 確認用コード
p INPUT1
p OUTPUT1
p INPUT2
p OUTPUT2
# > "5\nball\nstrike\nball\nstrike\nstrike\n"
# > "ball!\nstrike!\nball!\nstrike!\nout!\n"
# > "6\nball\nstrike\nball\nball\nstrike\nball\n"
# > "ball!\nstrike!\nball!\nball!\nstrike!\nfourball!\n"

続いて問題を解くメソッド( solve メソッドとします)を変数の下に定義します。

まず、入力データを受け取る処理を書きます。

入力された文字列を改行で区切り、1行目は使わないので _ に代入、2行目以降の文字列を配列 calls に代入します。

def solve(input_lines)
  # 改行区切りで分割
  # 1行目を _ に代入(使わないので捨てる)
  # 2行目以降を配列 calls に代入
  _, *calls = input_lines.split("\n")
  
  # 動作確認用コード
  calls
end

p solve(INPUT1)
# > ["ball", "strike", "ball", "strike", "strike"]

配列 calls にデータが正しく受け取れていれば、solve メソッドにストライク・ボールの判定処理を追加します。

strike_count, ball_count の変数を 0 で初期化した後、calls の先頭から次々と判定して結果を配列で返す処理を追記していきます。

def solve(input_lines)
  # 改行区切りで分割
  # 1行目を _ に代入(使わないので捨てる)
  # 2行目以降を配列 calls に代入
  _, *calls = input_lines.split("\n")
  
  # カウント初期化
  strike_count, ball_count = 0, 0
  
  # calls の先頭から次々判定して結果を配列で返す
  calls.each.map do |call|
    if call == "strike"
    # strike の場合
      strike_count += 1
      if strike_count == 3
        # strike_count が 3 なら out!
        "out!"
      else
        # strike_count が 3 未満なら strike!
        "strike!"
      end
    else
      # ball の場合
      ball_count += 1
      if ball_count == 4
        # ball_count が 4 なら fourball!
        "fourball!"
      else
        # ball_count が 4 未満なら ball!
        "ball!"
      end
    end
  end
end

p solve(INPUT1)

この時点で p solve(INPUT1)puts solve(STDIN.read) に変更すれば正解になりますが、p solve(INPUT1) == OUTPUT1 -> true を確認するために、ひと手間加えてみます。

当たり判定結果を一旦 result へ代入し、result の各要素を改行で連結して、末尾に改行を追加したものを返すように変更します。

def solve(input_lines)
  # 改行区切りで分割
  # 1行目を _ に代入(使わないので捨てる)
  # 2行目以降を配列 calls に代入
  _, *calls = input_lines.split("\n")
  
  # カウント初期化
  strike_count, ball_count = 0, 0
  
  # calls の先頭から次々判定して結果の配列を result に代入する
  result = calls.each.map do |call|
    if call == "strike"
      # strike の場合
      strike_count += 1
      if strike_count == 3
        # strike_count が 3 なら out!
        "out!"
      else
        # strike_count が 3 未満なら strike!
        "strike!"
      end
    else
      # ball の場合
      ball_count += 1
      if ball_count == 4
        # ball_count が 4 なら fourball!
        "fourball!"
      else
        # ball_count が 4 未満なら ball!
        "ball!"
      end
    end
  end
  result.join("\n") << "\n"
end

p solve(INPUT1)
p solve(INPUT1) == OUTPUT1
puts solve(INPUT1)
# > "ball!\nstrike!\nball!\nstrike!\nout!\n"
# > true
# > ball!
# > strike!
# > ball!
# > strike!
# > out!

確認用コードを削除して、標準入力からのデータ受け取りに変更と動作確認をしたら提出します。
STDIN.read の 入力終了は Ctrl+D 又は Ctrl+Z)

解答コード
def solve(input_lines)
  # 改行区切りで分割
  # 1行目を _ に代入(使わないので捨てる)
  # 2行目以降を配列 calls に代入
  _, *calls = input_lines.split("\n")
  
  # カウント初期化
  strike_count, ball_count = 0, 0
  
  # calls の先頭から次々判定して結果の配列を result に代入する
  result = calls.each.map do |call|
    if call == "strike"
      # strike の場合
      strike_count += 1
      if strike_count == 3
        # strike_count が 3 なら out!
        "out!"
      else
        # strike_count が 3 未満なら strike!
        "strike!"
      end
    else
      # ball の場合
      ball_count += 1
      if ball_count == 4
        # ball_count が 4 なら fourball!
        "fourball!"
      else
        # ball_count が 4 未満なら ball!
        "ball!"
      end
    end
  end
  result.join("\n") << "\n"
end

puts solve(STDIN.read)
他の解答例

"out!" でなければ "strike!""fourball!" でなければ "ball!" のような単純な分岐は三項演算子を使うとスッキリ書けます。
また、集計をハッシュにすればベースボールカウントとして strikeball をまとめて管理できます。

def solve(input_lines)
  # 改行区切りで分割
  # 1行目を _ に代入(使わないので捨てる)
  # 2行目以降を配列 calls に代入
  _, *calls = input_lines.split("\n")
  
  # カウント初期化
  count = { "strike" => 0, "ball" => 0 }
  
  # calls の先頭から次々判定して結果の配列を result に代入する
  result = calls.each.map do |call|
    if call == "strike"
      # strike の場合
      count["strike"] += 1
      # count["strike"] が 3 なら out! 3 以外(0~2) なら strike!
      count["strike"] == 3 ? "out!" : "strike!"
    else
      # ball の場合
      count["ball"] += 1
      # count["ball"] が 4 なら fourball! 4 以外(0~3) なら ball!
      count["ball"] == 4 ? "fourball!" : "ball!"
    end
  end
  result.join("\n") << "\n"
end

puts solve(INPUT1)

今回のまとめ

今回は分岐パターンが4種類でしたが、分岐条件をうまく整理出来ればあとは基本操作の組み合わせなので難しくないと思います。

分岐条件や処理が複雑だと感じたら、手書きで整理してみるのも良いと思います!

 

 



【PR】アルゴリズム学習でお世話になった本


アルゴリズム関連の技術書は大抵C/C++で書かれていますが、ある程度プログラムが出来れば、処理の流れは理解することが出来ます。






通称: 螺旋本。C++で解説されています。AOJ(Aizu Online Judge)を運営している会津大学の渡辺准教授が書いた本です。データ構造や計算量の内容から丁寧に書いてありますのでアルゴリズム学習の最初の参考書としてオススメです。







通称: 蟻本。C++で解説されています。競技プログラミング中級者の定番書と言われていて、競技プログラミングで利用できる色々なアルゴリズムを学ぶことが出来ます。かなり高度な内容も含まれているので1冊分を完全に理解するのにはかなりの時間がかかりそうですが、手元に置いて何度も読み返しています。







通称: チーター本。C#, C++, JAVAでTopcoderの問題を解説しています。初心者~中級者向けに書かれているので解説が非常に丁寧です。







Python3でアルゴリズムを解説している本です。講義スタイルの本で、図やフローチャートを使ってアルゴリズムとデータ構造についてしっかりと説明されています。代わりにコードは少なめです。Pythonコードが読めなくても十分理解できると思います。


-paiza, プログラミング
-

© 2024 じゃいごテック