トヨタ自動車プログラミングコンテスト2023#1(AtCoder Beginner Contest 298)をPythonで解く

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

Atcoder Python

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

こんにちは, Shinoryoです.

今回はトヨタ自動車プログラミングコンテスト2023#1(AtCoder Beginner Contest 298)を, Pythonで解いてみた結果を書き連ねていこうと思います.

A - Job Interview

面接官の評価「良」の数をA, 面接官の評価「良」の不可の数をBとすると, Yesとなる条件はA1かつB=0となります.

# 入力
N = int(input())
S = input()
# 良、可、不可の数を調査
ryou = sum(1 for s in S if s == "o")
fuka = sum(1 for s in S if s == "x")
# 出力
if ryou >= 1 and fuka == 0:
print("Yes")
else:
print("No")

B - Coloring Matrix

行列Aを適切に回転したものと行列Bとを比べる問題です. 行列Aを回転することによりできる行列には, 以下の4つがあります.

  • 行列A
  • 行列Aを反時計回りに90回転させたもの
  • 行列Aを反時計回りに180回転させたもの
  • 行列Aを反時計回りに270回転させたもの

したがって, 上記4つの行列と行列Bとをそれぞれ比べればよいことが分かります.

Pythonでは2次元配列の回転は, numpynp.rot90を用いることで簡単に実装できます. また, Ai,j=1である全ての(i,j)に対してBi,j=1であるかどうかを確かめるには, 行列BAの最小値が0以上であるかどうかを調べればよいことも分かります.

import numpy as np
# 入力
N = int(input())
A = np.array([list(map(int, input().split())) for _ in range(N)])
B = np.array([list(map(int, input().split())) for _ in range(N)])
# 0~3回回転させて、「Aが1ならばBが1」が成り立つか確認
# BからA_rotを引いたものに-1が含まれていれば、「Aが1なのにBが0」ということ(以外ありえない)
for l in range(4):
if np.min(B - A) >= 0:
print("Yes")
exit(0)
A = np.rot90(A)
print("No")

配列の回転を愚直に実装することもできます.

# N×Nの配列list2Dを反時計回りに90度回転させる関数
def list2D_rot90(list2D, N):
rot90_list2D = [[0] * N for _ in range(N)]
for i in range(N):
for j in range(N):
rot90_list2D[N - j - 1][i] = list2D[i][j]
return rot90_list2D
# 入力
N = int(input())
A = [list(map(int, input().split())) for _ in range(N)]
B = [list(map(int, input().split())) for _ in range(N)]
# 0~3回回転させて、「Aが1ならばBが1」が成り立つか確認
# BからA_rotを引いたものに-1が含まれていれば、「Aが1なのにBが0」ということ(以外ありえない)
for l in range(4):
ok = True
for i in range(N):
for j in range(N):
if B[i][j] - A[i][j] < 0:
ok = False
if ok:
print("Yes")
exit(0)
A = list2D_rot90(A, N)
print("No")

C - Cards Query Problem

実装手法自体はシンプルです. 配列boxi(1iN)を用意し, ここには箱iに入っているカードの番号を格納します. また, 配列cardj(1j200000)を用意し, ここにはカードjが少なくとも1枚入っている箱の番号を格納します.

  • クエリ1ij
    1. boxijを格納する.
    2. cardjiが含まれていなければ, cardjiを格納する.
  • クエリ2i
    1. boxiを昇順にソートしたものを出力する.
  • クエリ3i
    1. cardiを昇順にソートしたものを出力する.
# 入力
N = int(input())
Q = int(input())
# N個の箱の用意
box = [[] for _ in range(N + 1)]
# ある番号が格納されている箱の数を格納
card = [[] for _ in range(200000 + 1)]
# クエリの処理
for i in range(Q):
query = list(map(int, input().split()))
if query[0] == 1:
box[query[2]].append(query[1])
if query[2] not in card[query[1]]:
card[query[1]].append(query[2])
elif query[0] == 2:
print(" ".join(list(map(str, sorted(box[query[1]])))))
else:
print(" ".join(list(map(str, sorted(card[query[1]])))))

クエリ1のたびにソートを行ってしまうとTLEです.

import bisect
# 入力
N = int(input())
Q = int(input())
# N個の箱の用意
box = [[] for _ in range(N + 1)]
# ある番号が格納されている箱の数を格納
card = [[] for _ in range(200000 + 1)]
# クエリの処理
for i in range(Q):
query = list(map(int, input().split()))
if query[0] == 1:
bisect.insort_left(box[query[2]], query[1])
if query[2] not in card[query[1]]:
bisect.insort_left(card[query[1]], query[2])
elif query[0] == 2:
print(" ".join(list(map(str, box[query[1]]))))
else:
print(" ".join(list(map(str, card[query[1]]))))

D - Writing a Numeral

基本的には, 各クエリの処理に対して忠実になればよいです. 以下, 文字列Sを整数型の配列として格納したものをS, 整数型として格納したものをansのようにします(例えば, S[1,2,3,4,5], ans12345のようになります).

  • クエリ1x
    • ans=ans×10+x
    • Sの末尾にxを追加
  • クエリ2
    • Sの要素数を取得し, これをlとする
    • Sの先頭の要素を削除し, これをyとする
    • ans=ansy10l
  • クエリ3
    • ansを出力する

mod=998244353で割った余りを求めることを忘れないようにすることに注意しましょう. あとは高速化のためにいくつかの点に注意するだけです.

  • 配列Sは先頭または末尾にしかアクセスしないので, collectionsモジュールのdequeを用いるとよい.
  • 10lを求める際に, これがあまりにも大きくなってしまうので, modで割った余りにしてとよい. これを効率よく求めるために, Pythonの組み込み関数のpow()を用いることができる.
from collections import deque
# 入力
Q = int(input())
S = deque([1])
ans = 1
mod = 998244353
# 各クエリを順に処理する
for _ in range(Q):
query = list(map(int, input().split()))
if query[0] == 1:
S.append(query[1])
ans = (ans * 10 + query[1]) % mod
elif query[0] == 2:
ans = (ans - pow(10, len(S) - 1, mod) * S.popleft()) % mod
else:
print(ans)

なぜpow()の方が速いのか?

pow()の方が速いのは, 繰り返し2乗法と呼ばれる方法が実装されているからです. 例えば, 107

(1)107=10×10×10×10×10×10×10

のようにしてしまうと, 指数をNとして計算量がO(N)になってしまいます. ここで, 7=22+21+20であることに着目すると,

(2)1020=10,(3)1021=10×10,(4)1022=1021×1021,(5)1022+21+20=1022×1021×1020

のよう計算することができます. このようにすることで, 指数をNとして計算量をO(logN)に減らすことができます.

E以降

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

参考にしたサイト等