백준풀이-python

1916 최소비용 구하기

Fug 2021. 7. 19. 15:17

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

#다익스트라

#우선순위큐

 

1916번: 최소비용 구하기

첫째 줄에 도시의 개수 N(1 ≤ N ≤ 1,000)이 주어지고 둘째 줄에는 버스의 개수 M(1 ≤ M ≤ 100,000)이 주어진다. 그리고 셋째 줄부터 M+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그

www.acmicpc.net

#1916 최소비용 구하기
import sys

#system stdin 과 maxsize를 불러오기 위해 수입

import heapq

#heapq 우선순위큐를 사용하기 위해 수입

input=sys.stdin.readline
push=heapq.heappush
pop=heapq.heappop

#빠른 코딩과 시간을 줄이기 위해

n=int(input())

m=int(input())

#n,m입력

cost=[[] for i in range(n+1)]

#cost라기보단 입력 받는 시작 끝 비용 을 저장하기 위한 리스트, heapq을 이용하고 비용으로 우선순위를 매기기위해서 시작도시 값을 인덱스로 받고 우선순위와 도착도시를 값으로 갖는 이중리스트로 생성

 

for i in range(m):

s,a,c=map(int,input().split())

cost[s].append([a,c])

 

#짠

 

start,end=map(int,input().split())

#시작도시와 도착도시를 받아주고

 

time_table=[sys.maxsize]*(n+1)

time_table[start]=0

#현재 시작도시부터 끝도시까지 걸리는 최소비용을 나타내주는 리스트 생성 여기서 시작도시에서 시작도시로 가는 비용은 0이기때문에 0으로 설정

 

heap=[(0,start)]

#힙큐 만듭니다. 다시 말하지만 비용이 앞으로 가야합니다.

 

***********************여기서 고생했음, 비용을 앞으로 뺀다는 생각을 못하고 그냥 취향껏 도착도시 앞으로 뺐음 1번 heapq의 이해에서 다룰예정************************************************************************************************

 

while heap:

#heap을 돌립니다

 

money,now_city=pop(heap)
#heap이 가진 값을 pop하고


if money >= time_table[end]:
break

#여기서 중요** 시간을 줄이는데 가장 효율적 간단히 말하자면 a에서 b로 가는 비용이 (예를들어 start=> a, b =>end 이런 과정 사이에 드는 비용) 현재 start에서 바로 end로 가는 비용이라던지 아니면 아예 다른 c로 거쳐서 가는 비용이런걸 따졌을 때 나오는 최소비용보다 클 경우 그냥 스킵하는 겁니다.

 

#왜냐 지금 하려는건 money가 일단 최소비용보다 작다는 가정하에 그럼 now_city로 start가 가는 비용까지 다 합치면 (start = > a = > end 일때 start = > a 의 비용) 최소비용보다 작은가를 보려는 것이기 때문이다.

 

for next_city,expense in cost[now_city]:

expense1=expense+money

if time_table[next_city] > expense1:

time_table[next_city]=expense1

push(heap,(expense1,next_city))

#이제 지금 pop한 현재도시로 가는 비용을 알고있으니 이 도시를 거쳐서 end로 가는게 최소비용인지를 생각해야합니다. 아까 시작도시를 인덱스로 도착도시와 비용을 저장했으니 그 인덱스안에 있는걸 전부 풀어서 expense1로 만든 후 현재의 최소비용과 비교합니다. 그 후 다익스트라이다 이것을 거치고 가는 과정을 다시 생각하기 위해서 push 해준다

 

print(time_table[end])

#end로 가는 최소비용 출력

 

#너무 힘든 시간이였다. 예전에 푼적있었는데 데이터가 바뀌면서 실패처리 됐다길래 시간초과를 없애기위해서 바득바득 갈면서 해냈다.

중요한 점 3가지 있다.

1. heap의 이해(우선순위 큐)

 

사진까지 있으면 좀 편한데 귀찮으니깐 글로 쓰겠다.

 

우선순위 큐란 말그대로 우선순위를 비교해서 큐를 돌린다는 것이다.

 

이  문제에서 사용된 heapq는 우선순위큐를 사용하기 위함인데 heap은 거꾸로 자라는 나무라고 생각하면 된다. 간단히

 

보면 1 2 3 4 5 6 이 있다고 하자 그럼

 

    1

 2    3

4 5 6

 

이런식으로 가지를 치게 된다. 위에 있는 것이 밑에 있는것보다 작기만 하면 되는것이다.

 

이는 1 2 3 6 4 5 7 89 이렇게 있다고 했을때

 

       1

    2     3

  6  4  5  7

89

 

이렇게도 될수 있다는 뜻이다. 만약 여기서 3을 넣으면 3과 6이 바뀌게 된다. 즉 바로 위에 있는 숫자가 그 아래 숫자보다 작으면 서로 바뀐다.

 

heap은 그래서 이런 문제에서는 순서는 관계없이 최소비용을 처리해주기위해서 존재하는 것이다.

 

2. 다익스트라의 이해

 

길을 찾는다고 했을때 그 다음의 경로가 어떻게 되던간에 일단 현재의 길에서 가장 최단시간걸리는 길을 찾는것

 

예를 들어 a => b = > c와 a=>c 두개의 길이 있고 각 길은 a>b 는 2 b>c는 1 a>c는 3이 걸린다고 했을때 다익스트라는 a>b먼저 볼것이다 왜냐하면 그것이 가장 최단시간걸리는 길이기 때문

 

3. 이것을 구현하는 방법

 

굉장히 뚜렷한 문제이니만큼 개념이 없으면 힘든 것같다. 우선

 

문제를 보았을때 n=int(input()) m=int(input())부터 해주고 생각을 해본다 . 도시의 개수와 버스의 개수가 가진 의미는 도

 

시에서 도시로 버스를 이용하는데 도시는 고정되어있고 버스는 각각 다 다르게 움직인다는 것 그리고 셋째줄부터 공백

 

을 구분해시작 끝 비용으로나오고 한 버스가 끝나면 다음 버스는 줄로 구분되서 나온다는 걸 생각해서 for문으로 돌리고

 

시작 끝 비용으로 한 for문 당 다 나눠주고 마찬가지로 한 for문 당 리스트에 [시작]=[끝,비용]으로 다 담는다고 생각한다.

 

그 후 갱신될 최소비용리스트를 작성한다. 초기값은 최대값으로 설정한다. 그리고 시작도시를 기준으로 해야하기에 시작

 

도시의 최소비용은 0으로 리스트에 미리 박아놓는다. 문제에 시작도시와 끝도시는 같을 수 없다고 되어있지 않기 때문에

 

필수적으로 넣어야한다. 이를 끝내면 heap에 비용,시작값을 넣은후 풀어준다. 여기서 중요한건 비용부터 넣어야한다는

 

것 그래야 최소비용기준으로 heap이 돌아가게 된다. 작은 순서대로 비용이 처리되면서 다익스트라를 사용하게 되는 것

 

이다. 하면서 기존의 최소비용이 현재 처리중인 heap에서 pop한 도시까지 가는 비용보다 ( 즉 heap에 있는 비용 + heap

 

에 나온 도시까지 가는 비용) 클 경우 갱신한다.

 

완료 힘들다