728x90
시간 제한 5초, 메모리 제한 512MB
# 조건
- 소싯적 호석이는 골목 대장의 삶을 살았다.
- 호석이가 살던 마을은 N 개의 교차로와 M 개의 골목이 있었다.
- 교차로의 번호는 1번부터 N 번까지로 표현한다.
- 골목은 서로 다른 두 교차로를 양방향으로 이어주며 임의의 두 교차로를 잇는 골목은 최대 한 개만 존재한다.
- 분신술을 쓰는 호석이는 모든 골목에 자신의 분신을 두었고, 골목마다 통과하는 사람에게 수금할 것이다.
- 수금하는 요금은 골목마다 다를 수 있다.
- 당신은 A 번 교차로에서 B 번 교차로까지 C 원을 가지고 가려고 한다.
- 호석이의 횡포를 보며 짜증은 나지만, 분신술을 이겨낼 방법이 없어서 돈을 내고 가려고 한다.
- 하지만 이왕 지나갈 거면, 최소한의 수치심을 받고 싶다.
- 당신이 받는 수치심은 경로 상에서 가장 많이 낸 돈에 비례하기 때문에, 결국 갈 수 있는 다양한 방법들 중에서 최소한의 수치심을 받으려고 한다.
- 즉, 한 골목에서 내야 하는 최대 요금을 최소화하는 것이다.
- 예를 들어, 위의 그림과 같이 5개의 교차로와 5개의 골목이 있으며, 당신이 1번 교차로에서 3번 교차로로 가고 싶은 상황이라고 하자.
- 만약 10원을 들고 출발한다면 2가지 경로로 갈 수 있다.
- 1번 -> 2번 -> 3번 교차로로 이동하게 되면 총 10원이 필요하고 이 과정에서 최대 수금액을 5원이었고, 1번 -> 4번 -> 5번 -> 3번 교차로로 이동하게 되면 총 8원이 필요하며 최대 수금액은 6원이 된다.
- 최소한의 수치심을 얻는 경로는 최대 수금액이 5인 경로이다.
- 하지만 만약 8원밖에 없다면, 전자의 경로는 갈 수 없기 때문에 최대 수금액이 6원인 경로로 가야 하는 것이 최선이다.
- 당신은 앞선 예제를 통해서, 수치심을 줄이고 싶을 수록 같거나 더 많은 돈이 필요하고, 수치심을 더 받는 것을 감수하면 같거나 더 적은 돈이 필요하게 된다는 것을 알게 되었다.
- 마을의 지도와 골목마다 존재하는 호석이가 수금하는 금액을 안다면, 당신이 한 골목에서 내야하는 최대 요금의 최솟값을 계산하자.
- 만약 지금 가진 돈으로는 절대로 목표 지점을 갈 수 없다면 -1 을 출력하라.
입력
- 첫 줄에 교차로 개수 N, 골목 개수 M, 시작 교차로 번호 A, 도착 교차로 번호 B, 가진 돈 C 가 공백으로 구분되어 주어진다.
- 이어서 M 개의 줄에 걸쳐서 각 골목이 잇는 교차로 2개의 번호와, 골목의 수금액이 공백으로 구분되어 주어진다.
- 같은 교차로를 잇는 골목은 최대 한 번만 주어지며, 골목은 양방향이다.
출력
- 호석이가 지키고 있는 골목들을 통해서 시작 교차로에서 도착 교차로까지 C원 이하로 가는 경로 중에, 지나는 골목의 요금의 최댓값의 최솟값을 출력하라.
- 만약 갈 수 없다면 -1을 출력한다.
제한
- 1 ≤ N ≤ 100,000
- 1 ≤ M ≤ 500,000
- 1 ≤ C ≤ 1014
- 1 ≤ 골목 별 수금액 ≤ 109
- 1 ≤ A, B ≤ N, A ≠ B
- 골목이 잇는 교차로의 번호는 서로 다르다.
# 접근 방법
- 일반적인 다익스트라로 풀게 되면 TL을 받는 문제이다.
- 문제 이름처럼 조금 더 효율적으로 최소 비용을 찾는 방법을 고민해보았다.
- 골목의 정보가 주어질 때 각 골목이 가지고 있는 비용을 COSTs set에 저장해주고 list로 변환 후 정렬을 해준다.
- 이후 매개변수 탐색을 통하여 한 골목에서 내야하는 최대 요금의 최소값을 찾아준다.
- left <= right를 만족하는 동안 while문을 돌면서 mid = (left+right)//2를 통하여 중간 값을 잡아준다.
- dijkstra 함수에 costs[mid]값을 넣어준 후 다익스트라를 실행해준다.
def dijkstra(min_val):
q = []
heappush(q, (0, C, A))
dist = [float('inf')] * (N+1)
dist[A] = 0
while q:
max_val, now_money, node = heappop(q)
if node == B:
return max_val
for next_node, next_money in graph[node]:
money = now_money - next_money
val = max(max_val, next_money)
if val > min_val:
continue
if money >= 0:
if dist[next_node] > max_val:
heappush(q, (val, money, next_node))
dist[next_node] = max_val
return -1
- 현재까지의 한 골목의 최대 비용의 최솟값, 남은 돈, 현재 노드를 한 세트로 q에 넣어준다.
- 만약 도착지에 도착했다면, 현재 max_val을 return 해주고 아니라면 다음 목적지를 찾아준다.
- 이 때, 남은 돈이 부족하거나 현재 골목에서의 최대값이 기록된 값보다 크다면 continue를 해준다.
- 그렇지 않다면 q에 넣어주고 dist 배열에 최댓값을 갱신해준다.
전체 코드
import sys
sys.stdin = open('input.txt')
si = sys.stdin.readline
from heapq import heappop, heappush
def solve():
def dijkstra(min_val):
q = []
heappush(q, (0, C, A))
dist = [float('inf')] * (N+1)
dist[A] = 0
while q:
max_val, now_money, node = heappop(q)
if node == B:
return max_val
for next_node, next_money in graph[node]:
money = now_money - next_money
val = max(max_val, next_money)
if val > min_val:
continue
if money >= 0:
if dist[next_node] > max_val:
heappush(q, (val, money, next_node))
dist[next_node] = max_val
return -1
N, M, A, B, C = map(int, si().split())
graph = [[] for _ in range(N+1)]
costs = set()
for _ in range(M):
a, b, c = map(int, si().split())
graph[a].append((b, c))
graph[b].append((a, c))
costs.add(c)
costs = sorted(list(costs))
result = float('inf')
left = 0
right = len(costs) - 1
while left <= right:
mid = (left+right) // 2
v = dijkstra(costs[mid])
if not v == -1:
result = min(result, v)
right = mid - 1
else:
left = mid + 1
print(result if not result == float('inf') else -1)
solve()
728x90
'ALGORITHM > 다익스트라, 벨만포드, 플로이드워셜' 카테고리의 다른 글
[백준 22865번] 파이썬 - 가장 먼 곳 (1) | 2023.10.24 |
---|---|
[백준 17368번] 파이썬 - 백도어 (0) | 2023.10.11 |
[백준 1445번] 파이썬 - 일요일 아침의 데이트 (1) | 2023.10.02 |
[백준 11265번] 파이썬 - 끝나지 않는 파티 (0) | 2023.09.15 |
[백준 16118번] 파이썬 - 달빛 여우 (0) | 2023.08.30 |