백준 1939 - 중량 제한 (Python3)
코드
from collections import deque
n, m = map(int, input().split())
graph = [[] for _ in range(n+1)]
for _ in range(m):
a, b, c = map(int, input().split())
graph[a].append((b, c))
graph[b].append((a, c))
for i in range(1, n+1):
graph[i].sort(reverse=True)
# 섬 2개 주어짐
# 편의상 start, end로 명명
start, end = map(int, input().split())
# bfs 구현
# bfs는 항상 start에서 시작한다
# 어차피 출발은 start에서만 하므로
# 굳이 따로 처리해줄 필요가 없다
# ==> 즉, 우리가 찾고자 하는 바는 중량 제한을 통과하는 경우
# ==> c에 대하여 이진 탐색을 수행할 뿐
# ==> bfs로는 목표한 중량으로 start => end까지 갈 수 있는지만 확인
def bfs(target):
# 해당 섬에 방문하였는지 체크할 리스트
visited = [False for _ in range(n+1)]
# start점 방문
visited[start] = True
q = deque()
q.append(start)
while q:
now = q.popleft()
# 시작점이 끝점과 동일하면 True 반환
# 즉, start -> end까지 배송에 성공함
if now == end:
return True
# 현재 섬과 연결돼있는 섬에 대한 정보를 탐색
for nx, nc in graph[now]:
# 방문한 적 없는 섬이고
# 목표한 하중(target == mid)보다 크거나 같다
# => 방문할 수 있다는 의미
if visited[nx] == False and target <= nc:
# 큐에 넣어줌
q.append(nx)
# 방문할 것이므로 True로 변경
visited[nx] = True
# now == end가 못 됐으니 False 반환
return False
# 이진 탐색 구현
def binary_search():
result = 0
# 문제에 중량 제한(c)의 최솟값, 최댓값이 명시돼있음
# _min = left,
# _max = right
_min, _max = 1, 1000000000
# 중량 제한을 이진 탐색으로 찾음
while _min <= _max:
# mid (target) 설정
mid = (_min + _max) // 2
# mid에 대한 bfs의 결과가 참이면
if bfs(mid):
result = mid
_min = mid + 1
else:
_max = mid - 1
return result
print(binary_search())
문제 해설
이진 탐색과 BFS을 함께 사용하여 푸는 문제.
문제를 풀 수 있는 알고리즘에 대한 힌트가 많이 주어진 것을 봤을 때, 다양한 풀이 방법이 존재하는 것 같은 문제이다. 난 그 중, BFS와 이진 탐색을 활용한 방법을 선택하였다. (이 방법이 가장 먼저 떠오르기도 했고, 구글링 과정 중 이 방법이 다익스트라 알고리즘에 비하여 속도가 더 빠른 것을 확인했음)
두 알고리즘을 활용하여 풀어야 하는 문제이다보니, 주어진 값들에 대하여 어떤 식으로 알고리즘을 적용해야 할지 파악하는 것이 꽤나 어려웠다.
이진 탐색으로 중량 c를 target으로 설정하고, 그 값에 대하여 BFS를 수행, 결과가 주어진 end까지 도달할 수 있다면 True를 반환하여 최종 정답 변수인 result에 target인 mid의 값을 넣어준다. 이 때, _min과 _max가 우리가 찾는 c 즉, 중량의 최댓값의 범위이므로 c의 값을 이진 탐색(파라메틱 서치)하여 찾는 것이 문제에서 원하는 정답을 찾을 수 있는 로직이다. 위의 과정을 수행하여 _min과 _max의 값을 변경하면서 수행하여 최종 정답을 구할 수 있다.
두가지 알고리즘을 한 번에 접하게 되는 문제는 처음인 것 같다. 이러한 문제를 많이 풀면 사고력 증진에 매우 도움이 될 것 같다. 끊임없이 연습하여 내 것으로 만들어야겠다.
댓글남기기