문제 정의
‘세일즈맨의 고민’ 문제는 주어진 도시들 사이의 거리 정보를 바탕으로, 세일즈맨이 모든 도시를
한 번씩 방문하고, 다시 출발 도시로 돌아오는 최소 경로를 찾는 문제입니다. 이는 최적화 문제로,
그래프 이론의 한 가지 형태인 외판원 문제(TSP, Traveling Salesman Problem)으로
알려져 있습니다. 이 문제는 NP-완전 문제로 알려져 있으며, 해결하기 위한 다양한 방법이 존재합니다.
문제 설명
다음과 같은 조건이 주어집니다:
- n개의 도시가 있음.
- 각 도시는 1에서 n까지의 정수로 표현됨.
- 도시 간의 거리는 유향 그래프 형태로 주어지며, 거리 정보는 이차원 배열로 표현됨.
- 세일즈맨은 모든 도시를 한 번씩 방문하고, 출발 도시로 돌아와야 함.
입력 포맷
첫 번째 줄에는 도시의 수인 n이 주어집니다. 그 다음 n개의 줄에는 각 도시 간의 거리 행렬이 주어집니다.
거리 행렬은 다음과 같이 구성됩니다:
0 10 15 20 10 0 35 25 15 35 0 30 20 25 30 0
위의 예시에서 첫 번째 줄은 4개의 도시가 있으며, 각 도시 간의 거리를 나타냅니다.
예를 들어, 도시 1과 도시 2 사이의 거리는 10, 도시 1과 도시 3 사이의 거리는 15임을 알 수 있습니다.
출력 포맷
최소 경로의 총 거리를 출력합니다.
문제 해결 접근법
이 문제는 다양한 방법으로 접근할 수 있지만, 여기서는 백트래킹과 동적 계획법(DP)을
이용한 방법을 설명하겠습니다. 백트래킹을 사용하여 모든 경로를 탐색하면서 최소 경로를
찾을 수 있지만, 도시 수가 많아질수록 계산량이 폭발적으로 증가하므로, 동적 계획법을 통한
메모이제이션을 함께 사용하면 효율성을 높일 수 있습니다.
알고리즘 구현
우선 순열을 이용한 백트래킹 기법을 통해 모든 경로를 생성한 후, 경로의 거리를 계산하여
최소값을 업데이트하는 방식으로 구현할 수 있습니다. 아래는 이 알고리즘을 스위프트로 구현한
예제 코드입니다.
func tsp(graph: [[Int]], visited: inout [Bool], currentIndex: Int, count: Int, cost: Int, ans: inout Int) { // 모든 도시를 방문한 경우 if count == graph.count && graph[currentIndex][0] > 0 { ans = min(ans, cost + graph[currentIndex][0]) return } for i in 0..0 { visited[i] = true tsp(graph: graph, visited: &visited, currentIndex: i, count: count + 1, cost: cost + graph[currentIndex][i], ans: &ans) visited[i] = false } } } func findMinCost(graph: [[Int]]) -> Int { var visited = [Bool](repeating: false, count: graph.count) var ans = Int.max visited[0] = true tsp(graph: graph, visited: &visited, currentIndex: 0, count: 1, cost: 0, ans: &ans) return ans } let graph = [ [0, 10, 15, 20], [10, 0, 35, 25], [15, 35, 0, 30], [20, 25, 30, 0] ] let result = findMinCost(graph: graph) print("최소 경로의 거리: \(result)")
코드 설명
위 코드는 다음과 같은 구조로 되어 있습니다:
-
tsp 함수: 재귀적으로 도시를 방문하는 모든 경로를 탐색하며,
현재 도시에서 방문하지 않은 모든 도시를 순회합니다. 방문한 도시의 비용이 최소일 경우
현재 경로의 비용을 최소값으로 업데이트합니다. -
findMinCost 함수: 초기화 기능을 하며, 전체 경로를 탐색하기 위해
첫 번째 도시(0)를 방문한 상태로 tsp 함수를 호출합니다.
복잡도 분석
이 알고리즘의 시간 복잡도는 O(n!)입니다. 이는 각 도시를 순회하고 모든 경로를 탐구해야 하기
때문입니다. n이 작을 경우에는 사용할 수 있지만, n이 커질 경우 비효율적입니다.
따라서 보다 효율적인 방법이 필요한 경우 동적 계획법을 적용하여 비트마스크를 사용하는
방식으로 복잡도를 줄일 수 있습니다. 이 방식은 O(n^2 * 2^n)의 시간 복잡도를 가지며,
n이 20 이하일 경우 실용적입니다.
결론
‘세일즈맨의 고민’ 문제는 현실의 다양한 최적화 문제와 연관이 깊으며, 효과적인 알고리즘 설계
과정을 통해 문제를 해결하는 데 중요한 통찰력을 제공합니다. 코딩 테스트 준비 시,
이와 같은 문제를 풀어봄으로써 그래프 알고리즘 및 동적 계획법에 대한 이해도를 높이고,
체계적인 문제 해결 능력을 기를 수 있습니다.