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

AlgorithmDFS

문제

항공권 목록 tickets가 주어진다. "ICN"에서 출발하여 모든 항공권을 사용하는 여행 경로를 반환한다. 가능한 경로가 여러 개이면 알파벳 순서가 앞서는 경로를 반환한다.

핵심

모든 간선(항공권)을 정확히 한 번씩 사용하는 경로를 찾는다. tickets를 정렬한 뒤 DFS 백트래킹으로 처음 완성된 경로가 정답이다.

풀이

#include <string>
#include <vector>
#include <algorithm>
using namespace std;

bool dfs(const string& cur, vector<vector<string>>& tickets,
         vector<bool>& used, vector<string>& path) {
    if (path.size() == tickets.size() + 1) return true;

    for (int i = 0; i < (int)tickets.size(); i++) {
        if (used[i] || tickets[i][0] != cur) continue;

        used[i] = true;
        path.push_back(tickets[i][1]);

        if (dfs(tickets[i][1], tickets, used, path)) return true;

        path.pop_back();
        used[i] = false;
    }
    return false;
}

vector<string> solution(vector<vector<string>> tickets) {
    sort(tickets.begin(), tickets.end());
    vector<bool> used(tickets.size(), false);
    vector<string> path = {"ICN"};
    dfs("ICN", tickets, used, path);
    return path;
}

메모

  • 왜 간선 visited인가
    • 일반 DFS 문제는 모든 노드를 방문한다. 이 문제는 모든 간선(항공권)을 사용해야 한다
    • 같은 출발-도착 쌍의 항공권이 여러 장 있을 수 있다 (멀티그래프). used[i]로 각 항공권의 사용 여부를 개별 추적한다
  • 백트래킹 동작
    • tickets를 정렬하면 같은 출발지에서 알파벳 순으로 시도한다. 처음 완성된 경로가 곧 정답이다
    • 경로가 막히면(path.size() < tickets.size() + 1인데 더 이상 갈 곳이 없으면) pop_back + used[i] = false로 되돌린다
    • return true로 첫 완성 경로를 찾는 즉시 재귀를 전부 종료한다
  • 오일러 경로
    • 이 문제는 오일러 경로(Eulerian path) 문제이기도 하다. Hierholzer 알고리즘으로 O(E log E)에 풀 수 있다
    • 자세한 설명은 Hierholzer 알고리즘 정리 참고

복잡도

풀이시간공간
DFS 백트래킹O(N!) 최악O(N)
HierholzerO(E log E)O(E)