BOJ_1939 중량제한
- TOC {:toc}
이 글은 백준 온라인 저지의 1939번 문제를 파이썬(Python)으로 풀이한 것을 모아놓은 글입니다.
일종의 연습 기록이며 제가 정답을 받은 코드와 참고할만한 다른 코드를 같이 기록합니다. 필요한 경우 코드에 대한 해설을 기록합니다만 코드는 통과했어도 해설은 틀릴 수 있기 때문에 가볍게 참고해주시길 부탁드립니다. 피드백은 편하신 방법으로 자유롭게 주시면 감사하겠습니다.
2021.06.01
틀린 풀이입니다. 정답은 참고 답안을 참고해주세요.
단계 | 시작 시각 | 끝난 시각 | 걸린 시간 |
---|---|---|---|
문제 이해 | 23:46:44 | 23:49:54 | |
풀이 생각 | 23:49:57 | 00:05:07 | |
코딩 | 20:34:07 | 22:18:18 | |
디버깅 1 | 22:18:48 | 23:18:23 | |
디버깅 2 | 12:08:09 | 12:59:00 |
import sys
sys.setrecursionlimit(10 ** 8)
input = sys.stdin.readline
def find(A):
global I, E
tmp = [0]
for des in I[A]:
B, w = des
if w:
i = I[B].index([A, w])
I[B][i][1] = 0
if B == E:
tmp.append(w)
else:
tmp.append(min(w, find(B)))
return max(tmp)
N, M = map(int, input().split())
I = {}
for _ in range(M):
a, b, c = map(int, input().split())
if not (a in I):
I[a] = []
if not (b in I):
I[b] = []
I[a].append([b, c])
I[b].append([a, c])
S, E = map(int, input().split())
print(find(S))
아이디어 & 풀이
특정 경로의 각 섬을 거치면서는 중량의 최솟값을, 각 경로끼리 비교할 때는 중량의 최댓값을 결괏값으로 반영할 수 있도록 한다.
- 입력받은 섬에 연결된 모든 섬에 대해서 각 섬으로 갈 수 있는 중량 제한을
tmp
리스트에 담는다.- 연결된 섬이 목적지(i.e.,
E
) 이면 해당 섬까지의 중량 제한w
를 - 목적지가 아니면 해당 섬을 다시 함수에 넣었을 때 반환한 값과 해당 섬까지의 중량 제한
w
중 최솟값을tmp
에 추가한다.
- 연결된 섬이 목적지(i.e.,
tmp
중 최댓값을 반환한다.
디버그
- 처음에 연결 여부를 이차원 배열로 구현했는데 이 때문에 메모리 초과가 났다.
- 딕셔너리 형으로 바꿨는데 런타임 에러가 났다.
피드백
통과 안 되는 케이스 찾아내고 올바르게 고쳐보기
- [[boj-2110]]{공유기 설치} 문제와 똑같이 무게를 먼저 정하고 그 무게에서 경로로 이동할 수 있는지를 확인하면서 적절한 무게를 찾아가는 방식으로 풀어야 하는 것이었다.
참고 답안 1
import sys
from collections import deque
input = sys.stdin.readline
# 그래프를 순회하는 함수
def bfs(w):
visited = [False] * (N + 1)
# 출발 노드를 queue에 넣는다.
queue = deque([S])
# 출발 노드의 visited 상태를 True로 바꾼다.
visited[S] = True
# queue가 존재하는 동안
while queue:
# queue의 첫 원소를 pop 한 뒤
a = queue.popleft()
# 해당 섬과 연결된 섬들을 순회하면서
for b, c in I[a]:
# 해당 섬의 visited 상태가 False이고
# 현재 섬과 해당 섬 사이의 제한 하중이 w보다 크거나 같으면
if not visited[b] and c >= w:
# 해당 섬으로 이동한다.
# 해당 섬의 visited 상태를 True로 바꾸고
visited[b] = True
# queue에 추가한다.
queue.append(b)
# 목표 노드의 visited 상태를 반환한다.
return visited[E]
N, M = map(int, input().split())
start = 1000000000
end = 1
I = [[] for _ in range(N + 1)]
# 섬 리스트를 구성한다.
for _ in range(M):
a, b, c = map(int, input().split())
I[a].append((b, c))
I[b].append((a, c))
# 최소 무게
start = min(start, c)
# 최대 무게
end = max(end, c)
S, E = map(int, input().split())
res = start
# 이진 탐색을 이용해 무게의 중간값을 찾는다.
while (start <= end):
mid = (start + end) // 2
if bfs(mid):
start = mid + 1
res = mid
else:
end = mid - 1
print(res)
아이디어 & 풀이
이진 탐색(binary search)을 이용하는 문제이다. 응용 방식은 [[boj-2110]]{공유기 설치} 문제와 동일하다.
탐색 대상은 물품의 중량 w
이고 중간값을 체크하는 과정은 다음과 같다.
bfs(w)
는 시작 섬S
에서부터 너비우선탐색(bfs) 방식으로 방문할 수 있는 모든 섬을 방문한다.- 현재 섬과 방문하려는 섬 사이의 다리 중량 제한이
w
보다 크거나 같으면 해당 섬을 방문할 수 있다. - 해당 섬을 방문할 때마다 방문 여부인
visited
를True
로 바꾼다.
- 현재 섬과 방문하려는 섬 사이의 다리 중량 제한이
- 목적지인 섬
E
의 방문 여부(i.e.,visited[E]
)를 함수의 결괏값으로 반환한다. bfs(w)
의 반환 값을 확인한다.True
이면 물품의 중량w
가 목적지E
를 도달하기에 충분히 작은 값이기 때문에 무게를 늘리기 위해start
를mid + 1
로 키운다.False
이면 중량w
가 너무 커 목적지E
에 도달하지 못했다는 뜻이므로 무게를 줄이기 위해end
를mid - 1
로 줄인다.
참고 답안 2
# 풀이 2-1
import sys
input = sys.stdin.readline
class Island:
def __init__(self, n):
# parent: [0, 1, ..., n)]
self.parent = list(range(n + 1))
def union(self, x, y):
self.parent[self.find(x)] = self.find(y)
def find(self, x):
# parent의 x번째의 값이 x가 아니면
if self.parent[x] != x:
# x번째 값을 그 값을 find 메소드로 넘긴 값으로 바꾼다.
self.parent[x] = self.find(self.parent[x])
# parent의 x번째 값을 반환한다.
return self.parent[x]
N, M = map(int, input().split())
I = Island(N)
# 입력받은 값으로 튜플을 생성한 것의 리스트를
# 중량 제한 순으로 정렬한다.
bridges = sorted([tuple(map(int, input().split())) for _ in range(M)], key=lambda x: x[2])
S, E = map(int, input().split())
ans = 0
while I.find(S) != I.find(E):
a, b, c = bridges.pop()
I.union(a, b)
ans = c
print(ans)
# 풀이 2-2: heap 사용
import sys
from heapq import heappush, heappop
input = sys.stdin.readline
def find(node):
if node == parent[node]:
return node
p = find(parent[node])
parent[node] = p
return p
def union(a, b):
pa, pb = find(a), find(b)
if pa != pb:
parent[pb] = pa
def solve(s, e):
while bridges:
w, n1, n2 = heappop(bridges)
w = abs(w)
union(n1, n2)
if find(s) == find(e):
return w
n, m = map(int, input().split())
parent = [i for i in range(n + 1)]
bridges = []
for _ in range(m):
s, e, w = map(int, input().split())
# 최대 힙을 사용해야 하므로 -w를 넣는다.
heappush(bridges, [-w, s, e])
s, e = map(int, input().split())
print(solve(s, e))
아이디어 & 풀이
각 섬의 연결을 관리하는 Island
클래스를 이용한다. Island
클래스는 다음과 같은 프로퍼티를 갖는다.
parent
: 현재 인덱스 번째 섬이 최종적으로 향하는 섬의 번호를 담는 리스트이다.- 초기화시 모든 섬이 연결되지 않았으므로 자기 자신을 향한다.
- 즉, 각 인덱스 번쨰 값은 그 인덱스 값이다. (e.g.,
parent = [0, 1, 2, ..., n]
)
find
는x
가 최종적으로 향하는 섬의 번호를 찾아서 그 값을 반환한다.- ‘최종적으로 향하는 섬’은 다른 섬으로 이어지지 않는(i.e.,
parent
값이 자기 자신인)섬을 의미한다. - 마지막 섬까지 도달하는 과정에서 경로에 있는 모든 섬의
parent
값을 해당 섬으로 바꾸게 된다.
- ‘최종적으로 향하는 섬’은 다른 섬으로 이어지지 않는(i.e.,
union
입력받은x
가 최종적으로 향하는 섬과y
가 최종적으로 향하는 섬을 연결한다.x
가 최종적으로 향하는 섬의parent
값이y
가 최종적으로 향하는 섬의 번호가 된다.
이를 이용해 건널 수 있는 무게의 ‘최댓값’을 찾는 과정은 다음과 같다.
- 입력받은 연결 정보를 중량 제한에 대해 정렬한다.
- 출발 섬
S
와 목적지E
를 입력받는다. - 다음의 과정을 반복하면서 최대 무게 값을 찾는다.
- 각 연결 정보를 중량 제한이 큰 것부터
pop
해Island
에 반영한다.union
으로a, b
에 따라parent
를 변경한다.a
가 향하는 마지막 섬과b
가 향하는 마지막 섬이 연결되고 그 경로의 섬들이 각각 마지막 섬을 가리키게 된다.
- 현재 무게
c
를ans
로 지정한다.
- 각 연결 정보를 중량 제한이 큰 것부터
- 연결 정보(
parent
)를 변경할 때마다find(S)
와find(E)
가 같은지 확인한다.- 두 값이 같은 것은
S
가 향하는 섬과E
가 향하는 섬이 같다는 것으로S
에서E
로 이동할 수 있다는 것을 의미한다. - 중량값이 큰 것부터 반영했기 때문에 위 경로의 중량 제한이 남은 경로의 중량 제한 중 최댓값이다.
- 그러므로 더이상 연결 정보를 반영하지 않고 반복을 종료한다.
- 두 값이 같은 것은
풀이 2-2는 클래스가 아닌 일반 함수로 find
와 union
을 구현했고, 정렬된 리스트 대신 최대힙을 이용했다.
참고 답안 3
import sys
from heapq import heappop, heappush
sys.setrecursionlimit(100000000)
input = sys.stdin.readline
def dijkstra(s):
global bridges
weight = [0] * len(bridges)
visited = [False] * len(bridges)
queue = [(-1000000000000, s)]
while True:
while queue:
c1, a = heappop(queue)
if not visited[a]:
break
else:
break
weight[a] = c1
visited[a] = True
for b, c2 in bridges[a]:
if not visited[b]:
# c 중 절댓값이 최솟값인 값을 넣어야 하므로
# c1, c2의 max를 구해서 넣는다.
heappush(queue, (max(c1, c2), b))
return weight
n, m = map(int, input().split())
bridges = [[] for _ in range(n + 1)]
for _ in range(m):
a, b, c = map(int, input().split())
# 최대 힙을 구현해야하므로 중량을 음수로 바꾼다.
bridges[a].append((b, -c))
bridges[b].append((a, -c))
s, e = map(int, input().split())
print(-dijkstra(s)[e])
-
ps-python failed