東京海上日動プログラミングコンテスト2023(AtCoder Beginner Contest 299)をPythonで解く

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

Atcoder Python

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

こんにちは, Shinoryoです.

今回は東京海上日動プログラミングコンテスト2023(AtCoder Beginner Contest 299)を, Pythonで解いてみた結果を書き連ねていこうと思います.

A - Treasure Chest

|の位置と*の位置を取得し, その大小関係をもとに判断すればよいです. |は2つあるので, 左側のものはfindメソッドで, 右側のものはrfindメソッドで検索します.

# 入力
N = int(input())
S = input()
# |と*を探す
tatebou_first = S.find("|")
tatebou_second = S.rfind("|")
ast_first = S.find("*")
# |と*の位置の大小関係をもとに出力
if tatebou_first < ast_first and ast_first < tatebou_second:
print("in")
else:
print("out")

B - Trick Taking

以下のステップに分けて考えるとよいでしょう.

  1. 勝者になりうるカードの色Aを調べる.
  2. 色がAのカードの中で, 最も大きい値のカードを調べる.

勝者になりうるカードの色Aは, 数値Tと配列Cの比較で調べることができます. CTが含まれればA=T, CTが含まれなければA=C1です.

その後, カードを一つひとつ調べていって, カードの色がAであれば最大判定に加えればよいです.

# 入力
N, T = list(map(int, input().split()))
C = list(map(int, input().split()))
R = list(map(int, input().split()))
# ルールに従って判断
# 勝負となるカードの色Aを決める
if T in C:
A = T
else:
A = C[0]
# カードの色がAであるもののうち、最も大きい人を探す
ans_index = 0
ans_num = 0
for i in range(N):
if C[i] == A:
if ans_num < R[i]:
ans_index = i
ans_num = R[i]
# 出力
print(ans_index + 1)

C - Dango

o-が両方含まれているかどうかで区別すると, この問題は非常に簡単になります.

  • 両方含まれていれば, oが連続している数の最大が答えになります.
  • いずれかが含まれていなければ, Sはダンゴ文字列になりえないので1が答えになります.

oが連続している数を調べるには, S-で分割してみればよいです. S.split("-")とすることで, ["ooo", "o","oooo","oo","ooooo", ...]のように, oが連続している部分を取り出すことができます. それぞれの要素に対してlen関数を適用したものの最大値をとれば, 答えが分かります.

# 入力
N = int(input())
S = input()
# oと-が両方含まれているかどうかで区別する
if "o" in S and "-" in S:
# 含まれていれば、oが連続している数の最大が答えになる
print(max(map(len, S.split("-"))))
else:
# どちらかが含まれていなければダンゴ文字列にならない
print(-1)

D - Find by Query

二分探索のようにして探していけばよいでしょう. 例えば, N=7, S=0010011というパターンを考えます. すると, 以下のようにしていくことで効率よく探せることが分かります.

  1. l=1, r=7とします.
  2. m=(l+r)/2=4とします.
  3. Sm=S4の値を調べます.
  4. Sm=S4=0なので, l=4と更新します.
  5. m=(l+r)/2=5とします(小数点以下は切り捨てます).
  6. Sm=S5の値を調べます.
  7. Sm=S5=0なので, l=5と更新します.
  8. m=(l+r)/2=6とします.
  9. Sm=S6の値を調べます.
  10. Sm=S4=1なので, r=6と更新します.
  11. l=5, r=6よりl+1=rとなったので, l=5を出力して終了します.

このように, Sl=0, Sr=1の条件のもとで少しずつlrの差を縮めていって, l+1=rとなったときに, lが求める答えとなるようにします. lrの真ん中の値mを計算してSmの値を取得してlまたはrの値を更新するのを1ループとすれば, 高々logNループでl+1=rとなります. N2×105なので, 高々18ループでl+1=rとなります.

# 入力
N = int(input())
# 二分探索で見つける
# 最初はleft = 0、right = N - 1
left = 1
right = N
# 限界20回なので一応20回ループ
for _ in range(20):
# 真ん中のindexのSを尋ねる
mid = (left + right) // 2
print("? " + str(mid))
# S_midが0なら、leftをmidにする
if int(input()) == 0:
left = mid
# S_midが1なら、rightをmidにする
else:
right = mid
# leftとrightの差が1なら、ちょうどそこで0から1になっているので、それを出力
if left + 1 == right:
print("! " + str(left))
exit()

E以降

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

参考にしたサイト等