パナソニックグループプログラミングコンテスト2023(AtCoder Beginner Contest 301)をPythonで解く

投稿日:  更新日:2023/05/15

Atcoder Python

B!
Daniel AgreloによるPixabay(https://pixabay.com/)からの画像

こんにちは, Shinoryoです.

今回はパナソニックグループプログラミングコンテスト2023(AtCoder Beginner Contest 301)を, Pythonで解いてみた結果を書き連ねていこうと思います.

A - N-choice question

先にN/2回勝利した方が勝利になります. 高橋くんと青木くんのそれぞれが勝った回数をfor文で調べていき, どちらかがN/2回勝利した時点でその人を出力して終了すればよいです.

Pythonにおける切り上げは, math.ceilで簡単に得ることができます.

import math
N = int(input())
S = list(input())
T_count = 0
A_count = 0
for i in range(N):
if S[i] == "T":
T_count += 1
else:
A_count += 1
if T_count >= math.ceil(N / 2):
print("T")
exit()
elif A_count >= math.ceil(N / 2):
print("A")
exit()

B - Fill the Gaps

基本的にはA1,A2,,ANをそのまま出力し, もし途中で|AiAi+1|1となった場合にはその間を埋めるように出力すればよいことが分かります. Pythonのprint()は最後にデフォルトで改行を加えてしまうため, print(end = " ")のように最後に改行ではなく空白を加えるようにします.

# 入力
N = int(input())
A = list(map(int, input().split()))
# 隣接する2項の差を計算していく
for i in range(N - 1):
# 1ならそのまま出力する
if abs(A[i] - A[i + 1]) == 1:
print(A[i], end = " ")
# 1でなければ、その間を埋めるように出力する
else:
if A[i] < A[i + 1]:
print(" ".join(list(map(str, range(A[i], A[i + 1], 1)))), end = " ")
else:
print(" ".join(list(map(str, range(A[i], A[i + 1], -1)))), end = " ")
print(A[-1])

C - AtCoder Cards

  • 順番にSに含まれている文字がTにも含まれているかを確認する.
    • Sに含まれている文字がTにも含まれていればとりあえずOK. その文字を使うことにして, Tからその文字を消す.
    • Sに含まれている文字がTに含まれていれなければ, T@が含まれているかを確認する. T@が含まれていれば, その文字を使うことにして, Tからその文字を消す.
    • S@が含まれていれば, それはTにおける「余り物の」a, t, c, o, d, e, rあるいは@と対応させたいので, 個数を記録して保留にする.
  • Sに含まれている@の個数分, Tに「余り物の」a, t, c, o, d, e, rあるいは@が存在するかを調べる. 存在すれば, その文字を使うことにして, Tからその文字を消す.

以上を通過したらYes, どこかの段階で条件から外れたらNoを出力する.

ミソなのは, 「Sに含まれている文字がTにも含まれてい」るといったことを確認する手段です.

すでに使った文字が消去された新しいTを用いて検索をしなければなりません. しかし, ここにリストに対するinを使っていた場合, inの時間計算量はO(N)(Nはリストの要素数)であるため, 結果としてTLEになってしまいます.

# 入力
S = list(input())
T = list(input())
# 順番にSにある文字列がTにあるかどうかを調べる
# なかったら"@"で対応できる
# Sに"@"が含まれていたら対応するのはなんでもいいので、ひとまず保留
S_at_count = 0
for i in range(len(S)):
if S[i] == "@":
S_at_count += 1
elif S[i] in T:
index = T.index(S[i])
del T[index]
elif S[i] in ["a", "t", "c", "o", "d", "e", "r"] and "@" in T:
index = T.index("@")
del T[index]
else:
print("No")
exit()
# Sに"@"が含まれている数分、Tに対応するものがあるかを調べる
for i in range(S_at_count):
if "a" in T:
index = T.index("a")
del T[index]
elif "t" in T:
index = T.index("t")
del T[index]
elif "c" in T:
index = T.index("c")
del T[index]
elif "o" in T:
index = T.index("o")
del T[index]
elif "d" in T:
index = T.index("d")
del T[index]
elif "e" in T:
index = T.index("e")
del T[index]
elif "r" in T:
index = T.index("r")
del T[index]
elif "@" in T:
index = T.index("@")
del T[index]
else:
print("No")
exit()
# ここまで残っていたら"Yes"を出力
print("Yes")

これは, 文字列Tの情報を, 「文字xy個含まれている」といった情報に変換することです. これを辞書型で実装すれば, inの時間計算量はO(1)で済みます. 「Tから文字xを消す」はy1減らせばよく, y0の場合は文字xTに含まれていないものとみなすことにします.

「文字xy個含まれている」といった情報への変換は, collections.Counterを用いると簡単に実装できます.

from collections import Counter
# 入力
S = list(input())
T = list(input())
S_Counter = Counter(S)
T_Counter = Counter(T)
S_set = set(S)
T_set = set(T)
# 順番にSにある文字列がTにあるかどうかを調べる
# なかったら"@"で対応できる
# Sに"@"が含まれていたら対応するのはなんでもいいので、ひとまず保留
S_at_count = 0
for i in range(len(S)):
if S[i] == "@":
S_at_count += 1
elif S[i] in T_set and T_Counter[S[i]] > 0:
T_Counter[S[i]] -= 1
elif S[i] in ["a", "t", "c", "o", "d", "e", "r"] and "@" in T_set and T_Counter["@"] > 0:
T_Counter["@"] -= 1
else:
print("No")
exit()
# Sに"@"が含まれている数分、Tに対応するものがあるかを調べる
for i in range(S_at_count):
if "a" in T_set and T_Counter["a"] > 0:
T_Counter["a"] -= 1
elif "t" in T_set and T_Counter["t"] > 0:
T_Counter["t"] -= 1
elif "c" in T_set and T_Counter["c"] > 0:
T_Counter["c"] -= 1
elif "o" in T_set and T_Counter["o"] > 0:
T_Counter["o"] -= 1
elif "d" in T_set and T_Counter["d"] > 0:
T_Counter["d"] -= 1
elif "e" in T_set and T_Counter["e"] > 0:
T_Counter["e"] -= 1
elif "r" in T_set and T_Counter["r"] > 0:
T_Counter["r"] -= 1
elif "@" in T_set and T_Counter["@"] > 0:
T_Counter["@"] -= 1
else:
print("No")
exit()
# ここまで残っていたら"Yes"を出力
print("Yes")

D - Bitmask

まず, 全ての?0に変えたものが, Tの最小値になります. これによりもNが小さい場合, 1を出力することになります.

その条件をクリアしたら, Nを超えないように?1に変えていくことになります. 1に変えた?が左にあればあるほどその数字は大きくなりますので, 左の?から順に1に変えて, Nを超えないかを確かめていくことになります.

2進法の文字列を10進法の整数に変えるには, int(文字列, 基数)を用います.

# 入力
S = list(input())
N = int(input())
# まずは"?"を"0"にしたものを考える
# それがNより大きければ、これ以上小さくしようがないので-1を出力する
S_all_0_bin = "".join(list("0" if s == "?" else s for s in S))
ans = int(S_all_0_bin, 2)
if ans > N:
print(-1)
else:
# Nを超えないように、最初の"?"から順に"1"に置き換えていく
for i in range(len(S)):
if S[i] == "?":
S_add_bin = "".join(list("1" if j == i else "0" for j in range(len(S))))
S_add_dec = int(S_add_bin, 2)
if ans + S_add_dec <= N:
ans += S_add_dec
print(ans)

E以降

E以降は私の能力不足故に省略いたします.

参考にしたサイト等