반응형
[Gold IV] 떡 돌리기 - 20007
성능 요약
메모리: 119248 KB, 시간: 236 ms
분류
데이크스트라, 그래프 이론, 최단 경로, 정렬
제출 일자
2025년 2월 9일 01:42:25
문제 설명
군인인 성현이는 전역 후에 새 집으로 이사를 갔다. 주변 이웃과 친하게 지내고 싶은 마음에 이웃집에 떡을 돌리기로 했다. 떡은 한번에 하나씩만 들고 갈 수 있다. 집들 사이에는 총 M개의 양방향 도로가 있다. 귀찮은 성현이는 하루에 X보다 먼 거리를 걷지 않고 거리가 가까운 집부터 방문한다. 또 잠은 꼭 본인 집에서 자야 하므로 왕복할 수 없는 거리는 다음날 가기로 다짐한다. N-1개의 이웃집 모두에게 떡을 돌리기 위해서는 최소 며칠이 소요될 것인가.
집의 번호는 0번부터 N-1번까지 차례대로 붙어있다.
입력
첫째줄에 N, M, X, Y가 공백으로 구분되어 입력된다. (2 ≤ N ≤ 1,000, 1 ≤ M ≤ 100,000, 1 ≤ X ≤ 10,000,000, 0 ≤ Y < N)
두번째 줄부터 M+1번째 줄까지 A와 B 그리고 A집과 B집 사이의 도로의 길이 C가 주어진다. (0 ≤ A,B < N, 1 ≤ C ≤ 10,000) 단, A와 B는 서로 다른 수이고, C는 정수이다.
단, A집과 B집을 연결하는 도로는 유일하다.
출력
성현이의 집을 Y 라고 할 때, 이웃집 모두에 떡을 돌리기 위한 최소 일을 출력한다. 만약 모두 방문할수 없으면 -1을 출력한다.
풀이
import heapq
INF = float('inf')
def deijkstra(start, edges, N) :
q = []
heapq.heappush(q, (0, start))
distnaces = [INF] * N
distnaces[start] = 0
while q:
distance, node = heapq.heappop(q)
for (nxt_dist, nxt_node) in edges[node] :
new_dist = nxt_dist + distance
if distnaces[nxt_node] > new_dist :
distnaces[nxt_node] = new_dist
heapq.heappush(q, (new_dist, nxt_node))
return distnaces
def main() :
# 이웃집 개수, 양방향 도로 개수, 피로도 제한, 내 집
N, M, X, Y = map(int, input().split())
edges = [[] for _ in range(N)]
for _ in range(M) :
A, B, C = map(int, input().split()) # A집, B집, C거리
edges[A].append((C, B))
edges[B].append((C, A))
distances = deijkstra(Y, edges, N)
# 일, 이동 거리 계산
day = cost = 0
# 문제점 발생 : 갔다 온다는 설정은 어떻게 해야 할까.
# 집에 갔다가 그 자리에서 다시 나가는거 설정을 어떻게 할까
trips = []
for i in range(N) :
if i == Y :
continue
if distances[i] == INF :
print(-1)
return
trips.append(2 * distances[i])
trips.sort() # 가까운 집부터 처리
days = daily_sum = 0
for trip in trips :
# 하루 이동 한도보다 혼자 왕복 거리가 더 크다면 배달 불가능
if trip > X :
print(-1)
return
# 현재 날짜에 더 가도 한도를 넘지 않으면 같은 날 배달
if daily_sum + trip <= X :
daily_sum += trip
else :
# 한도를 넘는다면 하루를 마치고 다음 날 시작
days += 1
daily_sum = trip
# 남은 누적 이동 거리가 있다면 마지막 날 추가
if daily_sum > 0 :
days += 1
print(days)
main()
다익스트라 알고리즘 구현
def deijkstra(start, edges, N) :
q = []
heapq.heappush(q, (0, start))
distnaces = [INF] * N
distnaces[start] = 0
while q:
distance, node = heapq.heappop(q)
for (nxt_dist, nxt_node) in edges[node] :
new_dist = nxt_dist + distance
if distnaces[nxt_node] > new_dist :
distnaces[nxt_node] = new_dist
heapq.heappush(q, (new_dist, nxt_node))
return distnaces
주요 포인트
- 초기화:
- distances 리스트는 시작 노드(start)로부터 각 노드까지의 최단 거리를 저장합니다.
- 모든 노드는 초기값을 무한대(INF)로 설정한 후, 시작 노드의 거리는 0으로 설정합니다.
- 우선순위 큐 초기화:
- q = [(0, start)]
- (현재까지의 거리, 노드) 형태의 튜플을 사용합니다.
- 시작 노드부터 탐색하기 위해 (0, start)를 큐에 넣습니다.
- q = [(0, start)]
- 메인 루프 (while q:):
- heapq.heappop(q)로 가장 짧은 거리의 노드를 꺼냅니다.
- 이미 처리된 경로 확인:
- if distances[node] < dist: 조건을 사용하여, 현재 꺼낸 경로보다 이미 더 짧은 경로가 기록되어 있다면 해당 경로는 무시합니다.
- 인접 노드 탐색:
- 현재 노드의 모든 인접 노드에 대해,
- new_dist = dist + nxt_dist를 계산합니다.
- 만약 new_dist가 현재 저장된 distances[nxt_node]보다 작다면,
- distances[nxt_node]를 업데이트하고,
- 새로운 경로 정보를 (new_dist, nxt_node) 형태로 우선순위 큐에 추가합니다.
- 현재 노드의 모든 인접 노드에 대해,
- 리턴 값:
- distances 리스트를 반환하여, 시작 노드에서 각 노드까지의 최단 거리를 제공합니다.
메인 함수
입력 처리 및 그래프 구성
def main() :
# 이웃집 개수, 양방향 도로 개수, 피로도 제한, 내 집
N, M, X, Y = map(int, input().split())
edges = [[] for _ in range(N)]
for _ in range(M) :
A, B, C = map(int, input().split()) # A집, B집, C거리
edges[A].append((C, B))
edges[B].append((C, A))
다익스트라 호출 및 배달 대상 집 선정
distances = deijkstra(Y, edges, N)
trips = []
for i in range(N) :
if i == Y :
continue
if distances[i] == INF :
print(-1)
return
trips.append(2 * distances[i])
trips.sort() # 가까운 집부터 처리
- 최단 거리 계산 :
- dijkstra(Y, edges, N)를 호출하여, 내 집(Y번 집)에서 다른 모든 집까지의 최단 거리를 구합니다.
- 배달 대상 선정 :
- 내 집 (i == Y)은 배달 대상에서 제외
- 만약 어떤 집에 도달할 수 없다면(distances[i] == INF), 문제의 조건에 따라 -1을 출력하고 종료
- 왕복 거리 계산 :
- 각 집에 대해, 편도 거리 distances[i]에 2를 곱하여 왕복 거리를 계산한 후 trips 리스트에 추가
- 정렬 :
- trips.sort()를 호출하여, 가까운 집부터 배달 계획을 세울 수 있도록 정렬
하루 배달 스케줄 계산
days = daily_sum = 0
for trip in trips :
# 하루 이동 한도보다 혼자 왕복 거리가 더 크다면 배달 불가능
if trip > X :
print(-1)
return
# 현재 날짜에 더 가도 한도를 넘지 않으면 같은 날 배달
if daily_sum + trip <= X :
daily_sum += trip
else :
# 한도를 넘는다면 하루를 마치고 다음 날 시작
days += 1
daily_sum = trip
# 남은 누적 이동 거리가 있다면 마지막 날 추가
if daily_sum > 0 :
days += 1
print(days)
- 변수 설명 :
- days : 배달에 필요한 총 일수를 저장
- daily_sum : 현재 날에 누적하여 걸은 왕복 거리를 저장
- 루프를 통해 배달 스케줄 계산 :
- 각 배달 대상 집의 왕복 거리 trip에 대해 순회
- 단일 배달 불가능 조건 :
- 만약 한 집의 왕복 거리가 하루 이동 한도 X보다 크다면, 해당 집은 어떤 날에도 배달이 불가능 하므로 -1을 출력하고 종료
- 하루 내 배달 가능 여부 체크 :
- if daily_sum + trip <= X :
- 현재까지 걸은 거리(daily_sum)에 다음 집의 왕복 거리를 더했을 때 하루 한도 X이하이면, 같은 날 배달이 가능
- 이 경우 daily_sum에 해당 trip을 추가
- 새로운 날 시작 :
- 그렇지 않다면 (즉, daily_sum + trip > X 인 경우)
- 현재 날에는 더 이상 배달할 수 없으므로, days를 1 증가시켜 하루를 마감
- 그리고 daily_sum을 현재 trip 값으로 초기화하여 새로운 날의 첫 배달로 설정
- 그렇지 않다면 (즉, daily_sum + trip > X 인 경우)
- if daily_sum + trip <= X :
- 최종 처리 :
- 루프가 끝난 후, daily_sum이 0보다 크다는 것은 마지막 날에 배달한 집이 있다는 의미이므로, 마지막 날에 추가
- 최종적으로 days에 필요한 총 배달 일수를 출력
전체적인 동작 요약
- 입력 처리 및 그래프 구성 :
- 집의 개수, 도로 정보, 하루 이동 한도, 내 집의 번호를 입력받아 양방향 그래프를 구성
- 최단 거리 계산 (다익스트라 알고리즘) :
- 내 집(Y번 집)에서 모든 집까지의 최단 거리를 계산
- 만약 어떤 집이 도달 불가능 하다면, 바로 -1 출력
- 배달 대상 선정 및 왕복 거리 계산 :
- 내 집의 제외한 각 집의 편도 거리에 2를 곱하여 왕복 거리를 계산한 후, 가까운 순서대로 정렬
- 배달 일수 계산 (그리디) :
- 정렬된 왕복 거리들을 하나씩 더해가면서, 하루 이동 한도 X 내에 배달할 수 있는 만큼 처리
- 만약 추가 배달이 한도를 초과하면, 하루를 종료하고 다음 날로 넘김
- 하루 한도보다 한 번의 왕복 거리가 크면 배달 자체가 불가능 하므로 -1을 출력
- 결과 출력 :
- 배달에 필요한 최소 일수 출력
반응형
'알고리즘 스터디' 카테고리의 다른 글
[백준/파이썬][Bronze II] 번호표 교환 - 11949 (0) | 2025.02.12 |
---|---|
[백준/파이썬][Gold IV] PC방 요금 - 9080 (0) | 2025.02.11 |
[백준/파이썬][Gold V] 개업 - 13910 (0) | 2025.02.08 |
[백준/파이썬][Silver III] Champernowne Count - 27569 (1) | 2025.02.07 |
[백준/파이썬][Silver III] Contaminated Milk - 11972 (2) | 2025.02.07 |