AtCoder Beginner Contest 177をPythonで解く

投稿日:  更新日:2022/09/02

Atcoder Python

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

こんにちは, Shinoryoです.

今回はAtCoder Beginner Contest 177を, Pythonで解いてみた結果を書き連ねていこうと思います.

A - Don't be late

たどり着けるかどうかを計算します. 一応整数の範囲でやれるように

(1)DT×S

の条件式を用いています.

D, T, S = [int(x) for x in input().split()]
if D <= T*S :
print("Yes")
else:
print("No")

B - Substring

文字列Tを文字列Sに重ねる全パターンを調査して, その最小値をとります. 文字列の長さはlen()で分かります.

S = input()
T = input()
# 答えを仮置きしている
# この後minで最小値をとっていくので, 仮置きは最大の1000文字
ans = len(T)
# 調査するパターンを全探索(i文字目からを重ねてみる)
for i in range(len(S) - len(T)+1):
diff = 0
# 文字が異なるかどうかを確認(j文字目の文字が異なるかどうかを確認)
for j in range(len(T)):
if S[i + j] != T[j]:
diff += 1
ans = min(ans, diff)
print(ans)

C - Sum of product of pairs

問題文にあることを律儀に実行する(すなわち, 1i<jNを満たすi, jに対して,

(2)i,jAiAj

を計算しようとする)と, 計算量が膨大になり, TLEになります. 参考までにそのコードを示しておきます.

N = int(input())
A = [int(x) for x in input().split()]
Asum = [sum(A[i+1:]) for i in range(N-1)]
ans = 0
for i in range(N-1):
ans += (A[i] * Asum[i])
ans %= ((10**9) + 7)
print(ans)

解決策はいくつか考えられますが, 累積和によるものを示しておきます. 1iN(i=Nのときは対応するjが存在しないので, 実質的にはN1)の各iに対して, Aiに掛け算するのはAi+1, Ai+2, ……, ANです. ゆえに, あらかじめAi+1, Ai+2, ……, ANの和を求めておけば, 計算量を減らすことができます.

実際には, A1, A2, ……, ANの和を求めておいて, i=1に対する計算が終わった後にその累積和からA1を減らしてi=2に対する計算に利用する, というようにします.

# 入力
N = int(input())
A = [int(x) for x in input().split()]
# A[0]に掛け算するA[1], A[2], …, A[N-1]の和Asumを求める
Asum = sum(A[1:])
# 答えを入力する変数
ans = 0
# A[0], ……, A[N-2]に対して調査
for i in range(N-1):
# A[i]×(A[i+1], A[i+2], …, A[N-1])を計算
ans += (A[i] * Asum)
# 剰余を計算
ans %= ((10**9) + 7)
# AsumからA[i+1]を引いて, 次の計算に利用
Asum -= A[i+1]
# 答えを出力
print(ans)

あるいは,

(3)i<jAiAj=(iAi)2iAi22

であるため, これを計算する方法もあります.

N = int(input())
A = [int(x) for x in input().split()]
print(((sum(A)**2 - sum([x**2 for x in A]))//2) % (10**9 + 7))

D - Friends

友達関係を辿って辿り着ける人は友達であるということから, N人の人は最終的には友達同士で結成されたいくつかの「友達集合」に分けられます. もちろん, 友達が存在しないために1人の「友達集合」に含まれる可能性もあります.

ここで, もし同じ「友達集合」に所属する人同士を同じグループに配置してしまうと, その人達は友達同士になってしまうため, 条件を満たすことが出来ません. よって, 最も人数の多い「友達集合」の人数をKとすると, 必要なグループ数は最低でもKになります. 他の各「友達集合」についても, 1人ずつ順番にグループを割り振っていけばよいので, 求める数はKになります.

Kを求める方法として, ここでは幅優先探索(breadth first search; BFS)と呼ばれる方法を用います.

幅優先探索はグラフ理論等で用いられるアルゴリズムです. ある根ノードで始まり隣接した全てのノードを探索し, そのそれぞれに対して同様のことを繰り返すことによって探索対象ノードを網羅します.

下の図は, 鹿児島県から出発して, 陸続きの都道府県を探索する幅優先探索を表しています. 鹿児島県に隣接していて陸続きなのは, 熊本県と宮崎県です. その熊本県と宮崎県に対しても同様のことを繰り返していきます*1.

幅優先探索を九州で説明している図

いくつか注意点を述べておきます.

  • 友達が誰かを調べるべき人を格納したリスト(上の例でいうと, この後熊本県と宮崎県に関してどの都道府県に隣接しているかを調べるので, 「熊本県」と「宮崎県」を格納するリストが欲しい)に関しては, collectionsモジュールのdeque型を使用します*2.
  • これまでの探索でどの友達集合に所属しているかが判明している人に関しては, 新たに幅優先探索を行っても既知の友達集合を調べることになってしまいます. そのままではでは時間計算量が大きくなってしまうので, リストvisitedを用いてどの友達集合に所属しているかが判明している人のリストを管理します.
# collectionsモジュールのdeque型を使用
from collections import deque
# 入力
N, M = [int(x) for x in input().split()]
# 友達情報リストの作成
# AとBが友達なら, A行目のリストにBを加える
# リストのインデックスは0から始まるため, N+1行の2次元配列
# (0行目の存在は無視する)
g = [[] for _ in range(N+1)]
for i in range(M):
A, B = [int(x) for x in input().split()]
g[A].append(B)
g[B].append(A)
# どの友達集合に所属しているかが判明している人のリスト
visited = [False]*(N+1)
ans = 1
# 各人に対してループ
for i in range(1,N+1):
# どの友達グループに所属しているかが判明している場合は調べなくてよい
if visited[i]:
continue
visited[i] = True
# deque型の変数を作成(まずはiの友達を調べるため, iを格納)
queue = deque([i])
# iが所属する友達集合の人数を格納する変数を作成
# iとiは友達なので, 初期値は1
friends_number = 1
# 幅優先探索の実行
# queueの各要素について友達を調べる
while queue:
k = queue.popleft()
for j in g[k]:
# iの新しい友達jに関して,
# ・friends_numberに1を足し,
# ・visited[j]をTrueにし,
# ・queueにjを追加する
if visited[j]:
continue
friends_number += 1
visited[j] = True
queue.append(j)
ans = max(ans,friends_number)
print(ans)

E以降

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

参考にしたサイト等

脚注

*1 : 宮崎県から大分県に点線で矢印が引いてあるのは, 探索に関する熊本県と宮崎県の順番次第では, 大分県へは熊本県からではなく宮崎県から矢印が引かれる可能性があることを示しています.

*2 : deque型には, 先頭・末尾の要素を追加・削除するappend(), appendleft(), pop(), popleft()にかかる時間計算量が大きく減少するというメリットがあります. 一方で, deque型の中央部分へのアクセスには時間計算量が大きくなってしまうというデメリットがあるので, 要素の追加・削除以外も何らかの操作を行う場合には, deque型を使わない方が良いかもしれません.

Search

About Me

自分の写真
理系大学生でした. Bloggerを利用して発信を行っています.

Labels

Blog Archives