paiza プログラミング

[Ruby|Python]paiza paizaの森練習問題コンテスト過去問題5

paiza_forest_contest_005

paizaの森練習問題コンテストは、「paizaの森 (Discord)」にて約2か月に1回のペースで開催されており、ユーザー同士が開催時刻にpaizaの森に集まり「よーいドン」で与えられた数問の練習問題をお好きなプログラミング言語で解答する早さを競います。
( 第5回練習問題コンテストは D級3問, C級3問)
それほど難しい問題は出題されないですし、誤答によるペナルティーもありませんので初心者でも気軽に参加出来ます。
また、コンテストから数日後には出題された問題が一般公開され、だれでも挑戦できるようになります。

本記事で使用しているメソッド・アルゴリズムについて

解答例で使っているメソッドやアルゴリズムについて、下記の記事で詳しく解説していますので参考にしてみて下さい。

第5回 paizaの森練習問題コンテスト

1問目: 3 割打者 (paizaランク D 相当)

打席数 nヒットの回数 m が半角スペース区切りで与えられ、打率が 3 割以上なら "Yes" 、そうでないなら "No" を出力する問題です。

入出力例
  • 10 3が入力されたら、yesを出力する。(10 / 3 = 3.333...)
INPUT1 = <<"EOS"
10 3
EOS

OUTPUT1 = <<"EOS"
Yes
EOS
Ruby 解答例1-1

m / n.to_f で割り算結果を小数で求め、0.3 以上なら"Yes"、そうでないなら"No" を出力します。
(整数同士の割り算は整数が返り、整数と小数の割り算は小数が返るので、n を小数型に変換してから割っています)

# 解答例1-1
n, m = gets.split.map(&:to_i)

if m / n.to_f >= 0.3
  puts "Yes"
else
  puts "No"
end
Ruby 解答例1-2

小数型同士の割り算結果は小数が返ります。

# 解答例1-2
n, m = gets.split.map(&:to_f)

puts m / n >= 0.3 ? "Yes" : "No"
Python3 解答例1-1

python では整数同士の割り算で小数が返ります。(/ の場合)

# 解答例1-1
n, m = map(int, input().split())

if m / n >= 0.3:
    print("Yes")
else:
    print("No")
Python3 解答例1-2
# 解答例1-2
n, m = map(int, input().split())

print("Yes" if m / n >= 0.3 else "No")

 


2問目: テレワーク可能か (paizaランク D 相当)

スペース区切りでデスク使用率の整数 a天気の文字列 b が与えられ、デスクの使用率が 30(%) を超える場合、または天気が rainy (雨) の場合は"Yes"、そうでないなら "No" を出力する問題です。
なお、与えられる天気は rainy, sunny, cloudy のいずれかです。

入出力例
  • 60 sunnyが入力されたら、Yesを出力する。(デスク使用率が 60% なので)
INPUT1 = <<"EOS"
60 sunny
EOS

OUTPUT1 = <<"EOS"
Yes
EOS
Ruby 解答例1-1

if文の条件を || (or) で連結して分岐します。

# 解答例1-1
a, b = gets.split
a = a.to_i

if a > 30 || b == "rainy"
  puts "Yes"
else
  puts "No"
end
Ruby 解答例1-2
# 解答例1-2
a, b = gets.split

puts a.to_i > 30 || b == "rainy" ? "Yes" : "No"
Python3 解答例1-1
# 解答例1-1
a, b = input().split()
a = int(a)

if a > 30 or b == "rainy":
    print("Yes")
else:
    print("No")
Python3 解答例1-2
# 解答例1-2
a, b = input().split()

print("Yes" if int(a) > 30 or b == "rainy" else "No")

 


3問目: 文字列の反転 (paizaランク D 相当)

文字列 s が与えられ、s を逆順にした文字列を出力する問題です。

第3回コンテストでも同じ問題が出題されていますので今回はさくっと解説します。

入出力例
  • paizaが与えられたらaziapを出力する。
INPUT1 = <<"EOS"
paiza
EOS

OUTPUT1 = <<"EOS"
aziap
EOS
Ruby 解答例

reverseメソッドで逆順にします。

# 解答例
puts gets.chomp.reverse
Python3 解答例

スライスで逆順にします。

# 解答例
print(input()[::-1])

 


4問目: CD (paizaランク C 相当)

1行目で 曲の数 n が整数で与えられ、続く n 行で 半角スペース区切りで 曲の時間(分) m曲の時間(秒) s が与えられます。
CDの最大収録時間を74分とした場合、n 曲全てを 1 枚の CD に収録できる場合は "Yes" 、そうでなければ "No" を出力する問題です。

入出力例

15
1 7
5 22
5 23
5 4
4 30
3 58
3 55
4 17
3 30
5 0
3 53
3 38
5 25
4 14
7 16

が入力されたらYesを出力する。(曲の時間を全部足すと 66分32秒)

INPUT1 = <<"EOS"
15
1 7
5 22
5 23
5 4
4 30
3 58
3 55
4 17
3 30
5 0
3 53
3 38
5 25
4 14
7 16
EOS

OUTPUT1 = <<"EOS"
Yes
EOS
Ruby 解答例1-1

n 曲分の演奏時間を秒に換算して合計値を求め、74分=4440秒 以下かを判定しています。

# 解答例1-1
MAX_RECORDING_SEC = 60 * 74  # 4440 秒
n = gets.to_i

total_sec = 0
n.times do
  m, s = gets.split.map(&:to_i)
  total_sec += 60 * m + s
end

if total_sec <= MAX_RECORDING_SEC
  puts "Yes"
else
  puts "No"
end
Ruby 解答例1-1

解答例1-1を短くまとめた書き方です。

# 解答例1-2
MAX_RECORDING_SEC = 60 * 74  # 4440 秒
_, *songs = $stdin.read.split("\n").map { |r| r.split.map(&:to_i) }

total_sec = songs.map { |m, s| 60 * m + s }.sum

puts total_sec <= MAX_RECORDING_SEC ? "Yes" : "No"
Ruby 解答例2

二次元配列を縦に集計する書き方です。(分の合計と秒の合計をそれぞれ計算している)

# 解答例2
MAX_RECORDING_SEC = 60 * 74  # 4440 秒
_, *songs = $stdin.read.split("\n").map { |r| r.split.map(&:to_i) }

m, s = songs.transpose.map { |a| a.inject(:+) }
total_sec = 60 * m + s

puts total_sec <= MAX_RECORDING_SEC ? "Yes" : "No"
Python3 解答例1-1
# 解答例1-1
MAX_RECORDING_SEC = 60 * 74  # 4440 秒
n = int(input())

total_sec = 0
for _ in range(n):
    m, s = map(int, input().split())
    total_sec += 60 * m + s

print(total_sec)
if total_sec <= MAX_RECORDING_SEC:
    print("Yes")
else:
    print("No")
Python3 解答例1-2
# 解答例1-2
MAX_RECORDING_SEC = 60 * 74  # 4440 秒
_, *songs = [list(map(int, r.split())) for r in open(0).read().strip().split("\n")]

total_sec = sum([60 * m + s for (m, s) in songs])

print("Yes" if total_sec <= MAX_RECORDING_SEC else "No")
Python3 解答例2
# 解答例2
MAX_RECORDING_SEC = 60 * 74  # 4440 秒
_, *songs = [list(map(int, r.split())) for r in open(0).read().strip().split("\n")]

m, s = [sum(x[i] for x in songs) for i in range(len(songs[0]))]
total_sec = 60 * m + s

print("Yes") if total_sec <= MAX_RECORDING_SEC else print("No")

 


5問目: サイクルヒット (paizaランク C 相当)

野球での5打席分の結果が整数0~4で与えられ、整数が下記の対応になっているときに、サイクルヒットを達成していれば"Yes"、そうでなければ"No"を出力する問題です。
サイクルヒットは、シングルヒット、ツーベースヒット、スリーベースヒット、ホームラン全てを1回以上打つと成立します。

打席結果の対応

  • 1: シングルヒット
  • 2: ツーベースヒット
  • 3: ツリーベースヒット
  • 4: ホームラン
  • 0: それ以外
入出力例

4
3
2
0
1

が入力されたらYesを出力する。
(シングルヒット1本、ツーベースヒット1本、スリーベースヒット1本、ホームラン1本、それ以外1本)

INPUT1 = <<"EOS"
4
3
2
0
1
EOS

OUTPUT1 = <<"EOS"
Yes
EOS

 

Ruby 解答例1-1

各安打のフラグを用意して、該当すればtrueにし、最後に全ての安打フラグがtrueかを判定します。
(0はサイクルヒットに関係ないので無視します。)

# 解答例1-1
n = 5

single_hit = false
two_base_hit = false
three_base_hit = false
home_run = false
n.times do
  m_i = gets.to_i
  if m_i == 1
    single_hit = true
  elsif m_i == 2
    two_base_hit = true
  elsif m_i == 3
    three_base_hit = true
  elsif m_i == 4
    home_run = true
  end
end

if single_hit && two_base_hit && three_base_hit && home_run
  puts "Yes"
else
  puts "No"
end
Ruby 解答例1-2

解答例1-1のフラグを配列にした書き方。

# 解答例1-2
m = $stdin.read.split.map(&:to_i)

# 0: 単打, 1: 二塁打, 2: 三塁打, 3: 本塁打
ciclehit = Array.new(4, false)  # ciclehit = [false] * 4 でも OK

m.each { |res| ciclehit[res - 1] = true if res > 0 }

puts ciclehit.all? ? "Yes" : "No"
Ruby 解答例2-1

ハッシュを使って安打を集計する書き方。

# 解答例2-1
m = $stdin.read.split

results = Hash.new(0)
m.each { |res| results[res] += 1 if res != "0" }

puts results.length == 4 ? "Yes" : "No"
Ruby 解答例2-2

selectメソッド tallyメソッドを使って安打を集計する書き方。

# 解答例2-2
results = $stdin.read.split.select { |x| x != "0" }.tally

puts results.length == 4 ? "Yes" : "No"
Python3 解答例1-1
# 解答例1-1
n = 5

single_hit = False
two_base_hit = False
three_base_hit = False
home_run = False
for _ in range(n):
    m = int(input())
    if m == 1:
        single_hit = True
    elif m == 2:
        two_base_hit = True
    elif m == 3:
        three_base_hit = True
    elif m == 4:
        home_run = True

if single_hit and two_base_hit and three_base_hit and home_run:
    print("Yes")
else:
    print("No")
Python3 解答例1-2
# 解答例1-2
m = list(map(int, open(0).read().strip().split()))

# 0: 単打, 1: 二塁打, 2: 三塁打, 3: 本塁打
ciclehit = [False] * 4
for res in m:
    if res > 0:
        ciclehit[res - 1] = True

print("Yes" if all(ciclehit) else "No")
Python3 解答例2

辞書型で安打を集計する書き方。

# 解答例2
m = open(0).read().strip().split()

results = {}
for res in m:
    if res != "0":
        results[res] = results.get(res, 0) + 1

print("Yes" if len(results) == 4 else "No")
Python3 解答例3

辞書型(辞書内包表記)で安打を集計する書き方。

# 解答例3
if len({res for res in open(0).read().strip().split() if res != "0"}) == 4:
    print("Yes")
else:
    print("No")

 


6問目: 条件を満たす最小の整数 (paizaランク C 相当)

整数x, y, z が半角スペース区切りで与えられ、

x 以上かつ、y で割った余りが z である、最小の整数

を出力する問題です。

入出力例

11 7 2が入力されたら16を出力する。
11 ÷ 7 = 1 あまり 4
12 ÷ 7 = 1 あまり 5
13 ÷ 7 = 1 あまり 6
14 ÷ 7 = 2 あまり 0
15 ÷ 7 = 2 あまり 1
16 ÷ 7 = 2 あまり 2 ← 答え!

INPUT1 = <<"EOS"
11 7 2
EOS

OUTPUT1 = <<"EOS"
16
EOS
Ruby 解答例1-1

while文で、x ÷ y の余りが z になるか?を調べながら、x を 1ずつ増加させ、余りが z ならループを抜けて、 x を出力します。

# 解答例1-1
x, y, z = gets.split.map(&:to_i)

while x % y != z
  x += 1
end

puts x
Ruby 解答例1-2

timesメソッドで、解答例1-1と同じ処理をしています。

# 解答例1-2
x, y, z = gets.split.map(&:to_i)

(y - 1).times do
  break if x % y == z
  x += 1
end

puts x
Ruby 解答例2

計算で直接答えを求めます。

# 解答例2
x, y, z = gets.split.map(&:to_i)

puts x + (z - x % y) % y
Python3 解答例1-1
# 解答例1-1
x, y, z = map(int, input().split())

while x % y != z:
    x += 1

print(x)
Python3 解答例1-2
# 解答例1-2
x, y, z = map(int, input().split())

for _ in range(y-1):
    if x % y == z:
        break
    x += 1

print(x)
Python3 解答例2
# 解答例2
x, y, z = gets.split.map(&:to_i)

puts x + (z - x % y) % y

 


今回のまとめ

第5回コンテストでは、小数点数の割り算、複数条件での条件分岐、項目別に集計する問題などを解きました。
Cランク問題あたりから問題文も長くなってきますので、よく読んでしっかり理解してから解き始めましょう!

コンテスト時間は(たしか・・・)40分なので、Dランク3問、Cランク3問全問解くのはなかなか大変かも(*'ω'*)



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


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






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







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







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







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


-paiza, プログラミング

© 2024 じゃいごテック