Algorithms/Shortest path

[백준] 18352번 특정 거리의 도시 찾기 _ Python

wch_t 2023. 12. 27. 09:48

https://www.acmicpc.net/problem/18352

 

18352번: 특정 거리의 도시 찾기

첫째 줄에 도시의 개수 N, 도로의 개수 M, 거리 정보 K, 출발 도시의 번호 X가 주어진다. (2 ≤ N ≤ 300,000, 1 ≤ M ≤ 1,000,000, 1 ≤ K ≤ 300,000, 1 ≤ X ≤ N) 둘째 줄부터 M개의 줄에 걸쳐서 두 개

www.acmicpc.net

 

 

[초기 접근 방법]

- 특정 노드에서 출발하여, 최단 거리가 K인 모든 도시의 번호를 오름차순으로 출력

- 특정 노드이므로, bfs 탐색과 다익스트라 알고리즘을 사용할 수 있다.

 

 

[생각]

- bfs로는 금방 풀었지만,

   다익스트라 알고리즘 복습하는 문제로 좋았다.

 

 

[코드]

1) bfs

# 풀이 시간 : 20분
# 시간복잡도 : O(N+M)
# 공간복잡도 : O(N+M)
# 참고 : -

from collections import deque
import sys
input = sys.stdin.readline

def bfs():
    q = deque()
    q.append(X)

    # 출발지 노드 최단 거리는 '0'이다.
    # 문제의 조건으로, 출발 도시 X에서 출발 도시 X로 가는 최단 거리는 항상 0이라고 가정
    visited[X] = 0

    while q:
        start = q.popleft()

        # 인접 리스트
        for e in graph[start]:
            # 미방문 노드
            if visited[e] == -1:
                # visited 배열에, 출발지 노드에서 해당 노드까지의 최단거리 기록
                visited[e] = visited[start] + 1
                q.append(e)


# N : 도시의 개수 / M : 도로의 개수 / K : 거리 정보 / X : 출발 도시의 번호
N, M, K, X = map(int, input().split())

graph = [[] for _ in range(N+1)]
for _ in range(M):
    a, b = map(int, input().split())
    graph[a].append(b) # 단방향 도로이다.

# 방문 도로 체크
visited = [-1 for _ in range(N+1)]
bfs() # 출발 노드부터, 목적지 노드까지 최단거리를 구하기 위해 bfs

result = []
for i in range(N+1):
    if visited[i] == K:
        result.append(i)

# 최단 거리가 K인 모든 도시의 번호를 한 줄에 하나씩 오름차순으로 출력
if result:
    result.sort()
    for l in result:
        print(l)

# 하나도 존재하지 않으면 -1 출력
else:
    print(-1)

 

 

2) 다익스트라

# 시간복잡도 : O(MlogN) →각 간선마다 우선순위 큐에 삽입 연산이 일어남
#     1) 우선순위 큐에서 꺼낸 현재 노드에 연결된 간선 모두 확인 - 간선의 개수(E) 만큼 확인
#     2) 우선순위 큐에 간선을 넣고 빼는 과정 - logE
#     3) 따라선 모든 간선을 우선순위 큐에 넣고 뺀다고 했을 때 O(ElogE) 의 시간 복잡도를 갖는다.
#     4) 이때, 중복 간선을 포함하지 않는 경우, E는 항상 V^2 이하이다. (모든 노드가 연결 되어 있는 경우 V * (V-1))
#     5) logE < log(V^2)이다. log(V^2)은 2logV이기 때문에 O(logV)로 표현할 수 있다.
#     6) 따라서, 다익스트라 알고리즘 전체 시간 복잡도를 O(ElogV) 로 표현할 수 있다. (ElogE)
# 공간복잡도 : O(N+M)
# 참고 : -

import heapq
import sys
input = sys.stdin.readline

def dijkstra():
    q = []
    heapq.heappush(q, (0, X))
    distance[X] = 0
    
    while q:
        # 출발 거리, 노드 번호
        dist, node = heapq.heappop(q)

        # 이미 갱신된 거리보다 클 시, pass (우선순위에 밀린 원소들)
        if distance[node] < dist:
            continue

        for next_dist, next_node in graph[node]:
            cost = distance[node] + next_dist

            # 시작노드에서 다음노드까지 거리 vs 시작노드에서 현재노드까지 거리 + 인접 거리 
            if distance[next_node] > cost:
                distance[next_node] = cost
                heapq.heappush(q, (cost, next_node)) # cost가 갱신된 노드라면 다시 삽입


# N : 도시의 개수 / M : 도로의 개수 / K : 거리 정보 / X : 출발 도시의 번호
N, M, K, X = map(int, input().split())

graph = [[] for _ in range(N+1)]
for _ in range(M):
    a, b = map(int, input().split())
    graph[a].append((1, b)) # 단방향 도로이다.

# 출발지 노드를 중심으로, 각 노드까지의 최단 거리 저장
distance = [float('inf') for _ in range(N + 1)]

dijkstra()

# 결과 출력
result = []
for i in range(1, N+1):
    if K == distance[i]:
        result.append(i)

if result:
    for n in result:
        print(n)
else:
    print(-1)