paiza プログラミング

[Ruby]paiza スキルチェック過去問題セット 宝くじ (paizaランク C 相当)の解説

paiza解説 宝くじ

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

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

難易度は 1428±8 で、C級としては平均的な難易度だと思います。

宝くじ (paizaランク C 相当) を解いてみる

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

問題

今年もパイザ宝くじの季節がやってきました。パイザ宝くじ券には 100000 以上 199999 以下の 6 桁の番号がついています。毎年1つ当選番号 (100000 以上 199999 以下)が発表され、当たりクジの番号が以下のように決まります。

1等:当選番号と一致する番号
前後賞:当選番号の ±1 の番号(当選番号が 100000 または 199999 の場合,前後賞は一つしかありません)
2等:当選番号と下 4 桁が一致する番号(1等に該当する番号を除く)
3等:当選番号と下 3 桁が一致する番号(1等および2等に該当する番号を除く)

たとえば、当選番号が 142358 の場合、当たりの番号は以下のようになります。

  • 1等:142358
  • 前後賞:142357 と 142359
  • 2等:102358, 112358, 122358, …, 192358 (全 9 個)
  • 3等:100358, 101358, 102358, …, 199358 (全 90 個)

あなたが購入した n 枚の宝くじ券の各番号が入力されるので、それぞれの番号について、何等に当選したかを出力するプログラムを書いて下さい。

入力される値

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

b
n
a_1
a_2
:
a_n

ここで、b は当選番号、n は購入した宝くじの数、a_1,…,a_n は購入した宝くじ券の番号をそれぞれ表します。

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

期待する出力

期待する出力は n 行から成ります。各 i 行目 (1 ≦ i ≦ n) には、a_i が何等に当たったかに応じて、以下の文字列を出力して下さい。

1等の場合: first
前後賞の場合: adjacent
2等の場合: second
3等の場合: third
それ以外(外れ)の場合: blank

最後は改行し、余計な文字、空行を含んではいけません。

条件

すべてのテストケースにおいて、入力される値は全て整数であり、以下の条件をみたします。

  • 100,000 ≦ b ≦ 199,999
  • 1 ≦ n ≦ 100
  • 100,000 ≦ a_i ≦ 199,999 (1 ≦ i ≦ n)

入力例1
142358
3
195283
167358
142359
出力例1
blank
third
adjacent

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

入出力例をコピペしてヒアドキュメントで変数に代入し、データを確認します。きちんと受け取れていれば確認用コードは削除します。

INPUT1 = <<~"EOS"
  142358
  3
  195283
  167358
  142359
EOS
OUTPUT1 = <<~"EOS"
  blank
  third
  adjacent
EOS

# 確認用コード
p INPUT1
p OUTPUT1
# > "142358\n3\n195283\n167358\n142359\n"
# > "blank\nthird\nadjacent\n"

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

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

入力された文字列を改行で区切り整数型に変換し、1行目は当選番号 wining_num 、2行目は使わないので _ に代入、3行目以降を配列 tickets に代入します。

def solve(input_lines)
  # 改行区切りで分割した要素を整数型に変換
  # 1行目を wining_num に代入
  # 2行目を _ に代入(使わないので捨てる)
  # 3行目以降を tickets 配列に格納
  wining_num, _, *tickets = input_lines.split("\n").map(&:to_i)

  # 確認用コード
  [wining_num, tickets]
end

p solve(INPUT1)
# > [142358, [195283, 167358, 142359]]

1つ目の要素に当たり番号、2つ目の要素に購入チケットの配列がデータが正しく受け取れていれば、次に solve メソッドに当たりの判定処理を追加していきます。

tickets の先頭から次々と当たりを確認して結果を配列で返す処理を追記していきます。
整数型の下 n 桁は 数値 % 10 ** n で取り出すことが出来ます。 例: 123456 % 10 ** 4 -> 3456

def solve(input_lines)
  # 改行区切りで分割した要素を整数型に変換
  # 1行目を wining_num に代入
  # 2行目を _ に代入(使わないので捨てる)
  # 3行目以降を tickets 配列に格納
  wining_num, _, *tickets = input_lines.split("\n").map(&:to_i)

  # tikets 先頭から要素を次々取り出し処理結果を配列で返す
  tickets.each.map do |ticket|
    # 当たり確認
    if ticket == wining_num
      # 1等 当たり番号と一致
      "fitst"
    elsif ticket == wining_num - 1 || ticket == wining_num + 1
      # 前後賞 当たり番号のプラスマイナス1
      "adjacent"
    elsif ticket % 10 ** 4 == wining_num % 10 ** 4
      # 2等 下4桁が一致
      "second"
    elsif ticket % 10 ** 3 == wining_num % 10 ** 3
      # 3等 下3桁が一致
      "third"
    else
      # ハズレ
      "blank"
    end
  end
end

p solve(INPUT1)
# >["blank", "third", "adjacent"]

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

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

def solve(input_lines)
  # 改行区切りで分割した要素を整数型に変換
  # 1行目を wining_num に代入
  # 2行目を _ に代入(使わないので捨てる)
  # 3行目以降を tickets 配列に格納
  wining_num, _, *tickets = input_lines.split("\n").map(&:to_i)

  # tikets 先頭から要素を次々取り出し処理結果を配列で返し result へ代入
  result = tickets.each.map do |ticket|
    # 当たり確認
    if ticket == wining_num
      # 1等 当たり番号と一致
      "first"
    elsif ticket == wining_num - 1 || ticket == wining_num + 1
      # 前後賞 当たり番号のプラスマイナス1
      "adjacent"
    elsif ticket % 10 ** 4 == wining_num % 10 ** 4
      # 2等 下4桁が一致
      "second"
    elsif ticket % 10 ** 3 == wining_num % 10 ** 3
      # 3等 下3桁が一致
      "third"
    else
      # ハズレ
      "blank"
    end
  end
  # 要素を改行で連結し末尾に改行を追加して返す
  result.join("\n") << "\n"
end

p solve(INPUT1)
p solve(INPUT1) == OUTPUT1
puts solve(INPUT1)
# > "blank\nthird\nadjacent\n"
# > true
# > blank
# > third
# > adjacent

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

解答コード
def solve(input_lines)
  # 改行区切りで分割した要素を整数型に変換
  # 1行目を wining_num に代入
  # 2行目を _ に代入(使わないので捨てる)
  # 3行目以降を tickets 配列に格納
  wining_num, _, *tickets = input_lines.split("\n").map(&:to_i)

  # tikets 先頭から要素を次々取り出し処理結果を result へ代入
  result = tickets.each.map do |ticket|
    # 当たり確認
    if ticket == wining_num
      # 1等 当たり番号と一致
      "first"
    elsif ticket == wining_num - 1 || ticket == wining_num + 1
      # 前後賞 当たり番号のプラスマイナス1
      "adjacent"
    elsif ticket % 10 ** 4 == wining_num % 10 ** 4
      # 2等 下4桁が一致
      "second"
    elsif ticket % 10 ** 3 == wining_num % 10 ** 3
      # 3等 下3桁が一致
      "third"
    else
      # ハズレ
      "blank"
    end
  end
  # 要素を改行で連結し末尾に改行を追加して返す
  result.join("\n") << "\n"
end

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

文字列型の末尾 n 文字(下 n 桁)は 文字列 [-n..] で取り出すことが出来ます。 例: "123456"[-4..] -> "3456"

def solve(input_lines)
  # 改行区切りで分割した要素を整数型に変換
  # 1行目を wining_num に代入
  # 2行目を _ に代入(使わないので捨てる)
  # 3行目以降を tickets 配列に格納
  wining_num, _, *tickets = input_lines.split("\n")

  # tikets 先頭から要素を次々取り出し処理結果を result へ代入
  result = tickets.each.map do |ticket|
    # 当たり確認
    if ticket == wining_num
      # 1等 当たり番号と一致
      "first"
    elsif ticket == (wining_num.to_i - 1).to_s || ticket == (wining_num.to_i + 1).to_s
      # 前後賞 当たり番号のプラスマイナス1
      "adjacent"
    elsif ticket[-4..] == wining_num[-4..]
      # 2等 下4桁が一致
      "second"
    elsif ticket[-3..] == wining_num[-3..]
      # 3等 下3桁が一致
      "third"
    else
      # ハズレ
      "blank"
    end
  end
  # 要素を改行で連結し末尾に改行を追加して返す
  result.join("\n") << "\n"
end

puts solve(STDIN.read)

今回のまとめ

今回は下 n 桁の取り出し方と、複数の条件分岐( if ~ elsif ~ else )がポイントでした。

整数・文字列どちらのパターンでも対応できると、問題によって楽な方を選択できますね!



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


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






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







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







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







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


-paiza, プログラミング
-

© 2024 じゃいごテック