본문으로 바로가기

[프로그래머스] 여행경로

category ps/bfs & dfs 2021. 12. 20. 19:52
Categoryps/bfs & dfs
Tagbfs & dfs
Tags
난이도level 3
메모
발행 여부
사이트프로그래머스
이해완벽히 이해
최종 편집 일시
푼 날짜

문제 해설 및 주의사항

원문

문제 설명

주어진 항공권을 모두 이용하여 여행경로를 짜려고 합니다. 항상 "ICN" 공항에서 출발합니다.

항공권 정보가 담긴 2차원 배열 tickets가 매개변수로 주어질 때, 방문하는 공항 경로를 배열에 담아 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 모든 공항은 알파벳 대문자 3글자로 이루어집니다.
  • 주어진 공항 수는 3개 이상 10,000개 이하입니다.
  • tickets의 각 행 [a, b]는 a 공항에서 b 공항으로 가는 항공권이 있다는 의미입니다.
  • 주어진 항공권은 모두 사용해야 합니다.
  • 만일 가능한 경로가 2개 이상일 경우 알파벳 순서가 앞서는 경로를 return 합니다.
  • 모든 도시를 방문할 수 없는 경우는 주어지지 않습니다.

입출력 예

ticketsreturn
[["ICN", "JFK"], ["HND", "IAD"], ["JFK", "HND"]]["ICN", "JFK", "HND", "IAD"]
[["ICN", "SFO"], ["ICN", "ATL"], ["SFO", "ATL"], ["ATL", "ICN"], ["ATL","SFO"]]["ICN", "ATL", "ICN", "SFO", "ATL", "SFO"]

주의사항


풀이

ticketsreturn
[["ICN", "JFK"], ["HND", "IAD"], ["JFK", "HND"]]["ICN", "JFK", "HND", "IAD"]
[["ICN", "SFO"], ["ICN", "ATL"], ["SFO", "ATL"], ["ATL", "ICN"], ["ATL","SFO"]]["ICN", "ATL", "ICN", "SFO", "ATL", "SFO"]
  • ICN
  • SFO ATL

내 풀이 코드 (bfs) 예외 처리 전

import collections
def solution(tickets):
    answer = []
    
    graph = collections.defaultdict(list)
    for ticket in sorted(tickets, key= lambda x : (x[0], x[1]), reverse=True):
        graph[ticket[0]].append(ticket[1])
    
    Q = collections.deque()
    Q.append("ICN")
    while Q:
        current_node = Q.popleft()
        answer.append(current_node)
        if graph[current_node]:
            Q.append(graph[current_node].pop())
    
    return answer

내 풀이 코드 (dfs)

import collections
def solution(tickets):
    answer = []
    N = 0
    graph = collections.defaultdict(list)
    for ticket in sorted(tickets, key= lambda x : (x[0], x[1]), reverse=True):
        graph[ticket[0]].append(ticket[1])
        N += 1
    
    def dfs(current_node, path):
        # ticket 의 개수와 맞으면 정답 처리
        if len(path) == N + 1:
            return path
        # ticket 의 개수와 맞지 않으면서 다음 ticket 이 없다면
        if len(path) != N + 1 and len(graph[current_node]) == 0:
            return -1
        
        for _ in range(len(graph[current_node])):
            next_node = graph[current_node].pop() # 맨 끝 추출
            ret = dfs(next_node, path + [next_node])
            graph[current_node].insert(0, next_node) # 맨 앞에 복구(순서를 바꿔주기 위해서)
            if ret and ret != -1: # ret 이 존재하고, path 가 정상적으로 return 됐다면
                return ret
            
    answer = dfs("ICN", ["ICN"])
    return answer
  • 일반적인 dfs 구현은 수월했다.
    • 인접 노드를 깊이 우선으로 탐색한다.
      • 일반적인 dfs 와 달랐던 점은, 처음에 graph 를 사전 내림차순으로 정렬했기 때문에,
      • pop 을 활용하여 맨 뒤의 인접 노드부터 순회했다는 점이다. (그래야만이, 사전순으로 오름차순으로 순회가 가능하다.)
      • 맨 끝에서 pop 을 했기 때문에, 해당 인접 노드가 정답이 아니라면, 다시 insert 해주는 부분이 있다.
        • 맨 끝에 다시 insert 하지 않고 0번째 index 로 넣어서 사전순은 유지하되 순서는 바꾼다.
    # ticket 의 개수와 맞지 않으면서 다음 ticket 이 없다면
            if len(path) != N + 1 and len(graph[current_node]) == 0:
                return -1
    • 해당 코드를 잘 보자. 예외 처리. 즉, 히든 케이스를 잘 처리해야 하는 이유를 알 수 있다.

풀이 코드

from collections import defaultdict
def solution(tickets):
    # 특정 티켓의 인접 리스트를 구하는 함수
    def init_graph():
        routes = defaultdict(list)
        for key, value in tickets:
            routes[key].append(value)
        return routes

    # 재귀 호출을 사용한 DFS
    def dfs(key, footprint):
        if len(footprint) == N + 1:
            return footprint

        for idx, country in enumerate(routes[key]):
            routes[key].pop(idx)

            fp = footprint[:] # deepcopy
            fp.append(country)

            ret = dfs(country, fp)
            if ret: return ret # 모든 티켓을 사용해 통과한 경우

            routes[key].insert(idx, country) # 통과 못했으면 티켓 반환

    routes = init_graph()
    for r in routes:
        routes[r].sort()

    N = len(tickets)
    answer = dfs("ICN", ["ICN"])

    return answer

퇴고

반응형

'ps > bfs & dfs' 카테고리의 다른 글

[boj.kr] 최단경로  (0) 2022.02.04
[프로그래머스] 가장 먼 노드  (1) 2021.12.20
[프로그래머스] 순위  (0) 2021.12.20
[boj.kr] 10026. 적록색약  (0) 2021.12.20
77. Combinations  (0) 2021.12.12