프로그래머스 등급3 - 합승 택시 요금

합승 택시 요금 문제 바로 가기

풀이 날짜: 2026-05-08

baekjoon 알고리즘 문제 풀이

풀이 코드


import heapq

def solution(n, s, a, b, fares):
    INF = int(1e9)
    
    graph = [[] for _ in range(n+1)]
    
    for x, y, cost in fares:
            graph[x].append((y,cost))
            graph[y].append((x,cost))
    
    def dijkstra(start):
        dist = [INF] * (n+1)
        
        q = []
        heapq.heappush(q,(0,start))
        dist[start] = 0
        
        while q:
            d,now = heapq.heappop(q)
            
            if dist[now] < d:
                continue
            
            for next_node, fare in graph[now]:
                cost = d + fare
                if cost < dist[next_node]:
                    dist[next_node] = cost
                    heapq.heappush(q,(cost,next_node))
        
        return dist

    dist_s = dijkstra(s)
    dist_a = dijkstra(a)
    dist_b = dijkstra(b)
    
    answer = INF
    
    for i in range(1,n+1):
        answer = min(answer, dist_s[i] + dist_a[i] + dist_b[i])
    
    return answer
        
    

문제 핵심

처음 문제를 봤을 때는 “최단 거리” 문제라는 점에서 다익스트라 알고리즘을 떠올렸다. 시작 지점 s에서 A의 목적지 a, B의 목적지 b까지 이동해야 하고, 일부 구간은 함께 이동할 수 있기 때문에 단순히 s → a, s → b 최단거리만 구해서는 해결되지 않았다. 핵심은 “어디까지 함께 이동한 뒤 갈라지는 것이 가장 이득인가”를 찾는 것이었다.


접근 방법 (아이디어)

합승 택시는 중간 지점 k까지는 함께 이동하고, 이후 각자 목적지로 이동하는 구조라고 생각할 수 있다. 즉, 전체 비용은 다음처럼 나뉜다.

  • s → i : 함께 이동
  • i → a : A 혼자 이동
  • i → b : B 혼자 이동

따라서 모든 정점 i를 “합승을 끝내고 갈라지는 지점”이라고 가정해보고, 그 중 가장 비용이 작은 경우를 찾는 방식으로 접근했다.


그래프 구성

입력으로 주어지는 fares는 [출발지, 도착지, 비용] 형태였다. 또한 이 문제는 양방향 그래프이므로 모두 이동 가능하다. 그래서 인접 리스트 형태의 그래프를 만들고 양쪽 모두 저장했다.

  • graph[x].append((y, cost))
  • graph[y].append((x, cost))

다익스트라를 선택한 이유

처음에는 BFS도 떠올렸지만, 이 문제는 간선마다 비용이 서로 다르다. BFS는 모든 간선의 가중치가 동일할 때만 최단거리를 보장한다. 반면 이 문제에서는 요금(cost)이 다양하므로, 현재까지의 누적 비용이 가장 작은 경로부터 탐색하는 다익스트라 알고리즘이 적합했다.

우선순위 큐(heapq)를 사용해 가장 비용이 적은 경로를 먼저 꺼내도록 구현했다.

  • heapq.heappush(q, (비용, 노드))

다익스트라 함수 구현

다익스트라 함수에서는 먼저 모든 노드까지의 거리를 무한대로 초기화했다. 그리고 시작 노드의 거리를 0으로 설정했다. 이후 현재 비용이 가장 작은 노드를 꺼내고, 연결된 노드들을 확인하면서 더 짧은 경로가 발견되면 거리 배열을 갱신했다.

  • cost = d + fare

처음 헷갈렸던 부분

장 헷갈렸던 부분은 다음 코드였다.

  • dist_s = dijkstra(s)
  • dist_a = dijkstra(a)
  • dist_b = dijkstra(b)

처음에는 dijkstra(s)를 하면 s 하나의 값만 나오는 줄 알았다. 하지만 실제로 다익스트라는 “특정 시작점에서 모든 노드까지의 거리 배열”을 반환한다. 즉, dist_s[i]는 s에서 i까지의 최단거리라는 의미이다.


왜 모든 i를 확인하는가

합승 택시는 특정 지점까지 함께 이동하다가 갈라지는 구조이다. 그런데 어느 지점에서 갈라지는 것이 가장 저렴한지는 알 수 없다. 따라서 for i in range(1, n+1): 를 통해 모든 정점을 “학습 종료 지점”이라고 가정해보는 것이다. 그리고 dist_s[i] + dist_a[i] + dist_b[i]를 계산한다.


최종 비용 계산

모든 정점 i를 확인하면서 가장 작은 비용을 정답으로 선택했다.

  • answer = min(answer, dist_s[i] + dist_a[i] + dist_b[i])

즉 “어디까지 같이 이동하는 것이 가장 효율적인가”를 전부 시도한 뒤 최소값을 선택하는 방식이었다.


구현 과정에서 실수했던 부분

처음 구현할 때는 dist 배열을 함수 밖에 선언했는데, 이렇게 하면 다익스트라를 여러 번 실행할 때 이전 결과가 남아 문제가 발생했다. 따라서 dist는 반드시 dijkstra() 내부에서 새로 생성해야 했다.

배운 점

이번 문제를 통해 다익스트라가 단순히 한 목적지까지의 최단거리만 구하는 알고리즘이 아니라, “한 시작점에서 모든 노드까지의 최단거리 배열”을 반환한다는 점을 명확하게 이해할 수 있었다. 또한 문제를 “합승 후 분기” 구조로 단순화해서 생각하니 풀이 흐름이 훨씬 쉽게 보였고, 여러 시작점에서의 최단거리 결과를 조합해 새로운 문제를 해결할 수 있다는 점도 배울 수 있었다.