C - 感染経路の追跡 / Tracing Infection Routes Editorial by admin
Claude 4.5 Opus概要
グラフ上で始点から最大 \(K\) 回の移動で到達できる頂点の数を求める問題です。幅優先探索(BFS)を使って効率的に解くことができます。
考察
問題の言い換え
この問題は、グラフ理論の言葉で言い換えると「始点 \(S\) から距離 \(K\) 以内にある頂点の個数を求める」問題です。ここで「距離」とは、2頂点間の最短経路に含まれる辺の数を指します。
素朴なアプローチの問題点
もし深さ優先探索(DFS)で「\(K\) 回以内の移動で到達できるか」を判定しようとすると、同じ頂点を何度も訪れる可能性があります。例えば、頂点 \(A\) に距離 \(2\) で到達した後、別の経路で距離 \(3\) でも到達してしまい、無駄な探索が発生します。
解決策
幅優先探索(BFS) を使えば、各頂点への最短距離を効率的に求められます。BFSには「始点に近い頂点から順に探索する」という性質があるため、一度訪れた頂点を再び訪れる必要がありません。
アルゴリズム
グラフの構築: 接触関係を隣接リストで表現します。
BFSの実行:
- 始点 \(S\) をキューに入れ、距離を \(0\) に設定
- キューから頂点を取り出し、距離が \(K\) 以下ならカウントを増やす
- 隣接する未訪問の頂点について、距離を計算してキューに追加
距離 \(K\) 以下の頂点をカウント: BFSの過程で、距離が \(K\) 以下の頂点だけを数えます。
具体例
\(N=5, M=4, S=1, K=2\) で、辺が \((1,2), (2,3), (3,4), (1,5)\) の場合:
1 --- 2 --- 3 --- 4
|
5
- 距離 \(0\): 頂点 \(1\)(始点)
- 距離 \(1\): 頂点 \(2, 5\)(\(1\) の隣接頂点)
- 距離 \(2\): 頂点 \(3\)(\(2\) の隣接頂点)
- 距離 \(3\): 頂点 \(4\)(到達できるが \(K=2\) を超える)
答えは \(4\) 人(頂点 \(1, 2, 3, 5\))です。
計算量
- 時間計算量: \(O(N + M)\)
- 各頂点は高々1回キューに追加され、各辺は高々2回(両端点から1回ずつ)調べられます
- 空間計算量: \(O(N + M)\)
- 隣接リストに \(O(N + M)\)、距離配列とキューに \(O(N)\)
実装のポイント
距離の管理:
dist[v] = -1で未訪問を表し、訪問時に実際の距離を記録します。これにより訪問済み判定と距離管理を1つの配列で行えます。枝刈り: キューに追加する時点で
dist[u] <= Kをチェックすることで、\(K\) を超える距離の頂点をキューに入れないようにしています。これにより無駄な探索を防げます。1-indexed: 頂点番号が \(1\) から \(N\) なので、配列サイズを \(N+1\) にして 1-indexed で扱っています。
カウントのタイミング: キューから取り出した時点で距離が \(K\) 以下であることを確認してからカウントしています。
ソースコード
from collections import deque
def solve():
N, M, S, K = map(int, input().split())
# 隣接リストの構築
graph = [[] for _ in range(N + 1)]
for _ in range(M):
U, V = map(int, input().split())
graph[U].append(V)
graph[V].append(U)
# BFSで距離Kまでの頂点を数える
dist = [-1] * (N + 1)
dist[S] = 0
queue = deque([S])
count = 0
while queue:
v = queue.popleft()
if dist[v] > K:
break
count += 1
for u in graph[v]:
if dist[u] == -1:
dist[u] = dist[v] + 1
if dist[u] <= K:
queue.append(u)
print(count)
solve()
この解説は claude4.5opus によって生成されました。