• TOC {:toc}

이 글은 프로그래머스의 43164번 문제를 자바스크립트(JavaScript)으로 풀이한 것을 모아놓은 글입니다.

일종의 연습 기록이며 제가 정답을 받은 코드와 참고할만한 다른 코드를 같이 기록합니다. 필요한 경우 코드에 대한 해설을 기록합니다만 코드는 통과했어도 해설은 틀릴 수 있기 때문에 가볍게 참고해주시길 부탁드립니다. 피드백은 편하신 방법으로 자유롭게 주시면 감사하겠습니다.

2024.01.11

테스트 통과 시간 메모리
테스트 1 통과 16.83ms 37.6MB
테스트 2 통과 0.17ms 33.6MB
테스트 3 통과 0.27ms 33.6MB
테스트 4 통과 0.18ms 33.5MB
단계 시작 시각 끝난 시각 걸린 시간
문제 이해 16:10:29 16:12:00
풀이 생각 16:12:03 16:13:37
풀이 생각 20:18:59 20:57:31
코딩 20:58:42 22:01:36
디버그 22:03:56 22:24:53
function solution(tickets) {
    let answer = ["ICN"];

    // dfs: 경로 탐색
    function dfs(route, ticketCountMap) {
        // 반환 조건: 지나온 공항의 수가 티켓의 수보다 1개 많음
        if (route.length === tickets.length + 1) {
            answer = route;
            return;
        }

        // 새로운 출발지 지정: route의 마지막 원소
        const departure = route[route.length - 1];

        // 해당 출발지의 도착지(arrivalMap)에 대해
        const arrivals = ticketCountMap.get(departure) ?? [];
        arrivals.forEach((ticketCount, arrival, map) => {
            // 아직 티켓을 전부 소진하지 않았으면
            if (ticketCount) {
                // 티켓을 하나 소진하고
                map.set(arrival, map.get(arrival) - 1);
                // route에 해당 목적지를 추가해 dfs를 실행
                dfs([...route, arrival], ticketCountMap);
                // 순회 시에는 영향을 미치면 안되므로 원상복구
                map.set(arrival, map.get(arrival) + 1);
            }
        });
    }

    // 출발지: 해당 출발지에서 도착할 수 있는 도시 배열의 map을 구성
    const ticketMap = new Map([]);
    tickets.forEach(([a, b]) => {
        ticketMap.set(a, [...(ticketMap.get(a) ?? []), b]);
    });

    const ticketCountMap = new Map([]);
    // 각 출발지에 대해
    ticketMap.forEach((value, key) => {
        const arrivalsMap = new Map([]);
        // 도착 공항을 알파벳의 역순으로 순회하면서
        value
            .sort()
            .reverse()
            .forEach((arrival) =>
                // 도착지별 티켓 개수인 arrival map을 구성
                arrivalsMap.set(arrival, (arrivalsMap.get(arrival) ?? 0) + 1)
            );
        // 해당 arrivalMap을 각 출발지에 매핑
        ticketCountMap.set(key, arrivalsMap);
    });

    dfs(answer, ticketCountMap);
    return answer;
}

아이디어 & 풀이

공항을 모두 방문해야 하므로 "ICN"을 출발지점으로 각 티켓을 dfs로 탐색하면서 지나온 경로를 업데이트 한다.

  • 방문한 공항의 수가 티켓의 수보다 1개 많아지면 모든 공항을 방문한 것이므로 route를 반환한다.

공항의 방문 여부를 확인하기 위해 ticketCount map을 사용한다.

  • 일반적인 DFS에서는 방문 여부를 확인하기 위해 boolean 값인 isVisited를 사용하지만 이 문제의 경우 출발지와 도착지가 같은 티켓이 여러장 존재할 수 있으므로 각 출발지의 도착지로 갈 수 있는 티켓의 수를 사용하며 티켓이 모두 소진되면 다시 방문하지 않는다.
  • 재귀를 진행하면서 answerroute로 업데이트 하면 최종 answer는 마지막에 조건을 만족한 route가 되므로 map을 구성할 때 공항을 알파벳의 역순으로 등록해야 한다.

디버그

  • ticketCountMap.get(departure)시 마지막 departure의 경우 다른 곳으로 도착하는 티켓이 없다면 arrivals가 제대로 된 값이 아니게 되어 arrivals.forEach에서 런타임 에러가 발생하므로 꼭 별도로 처리를 해주어야 한다.
  • 동일한 출발지와 도착지를 갖는 티켓이 중복으로 존재할 수 있다는 것을 고려해야 한다.

피드백

  • 알파벳 순으로 순회하면서 처음 조건을 만족할 때 아예 해당 값을 반환해버리면 좋을 것 같다.