외판원 순회
https://www.acmicpc.net/problem/2098
문제
외판원 순회 (Traveling Salesman problem, TSP)
외판원 순회 문제는 여러 도시와 그 도시들 사이의 거리가 주어졌을 때, 모든 도시를 정확히 한 번씩 방문하면서 가장 짧은 거리를 걸으며 돌아오는 문제입니다.
입력
- 첫째 줄에 도시의 수 N이 주어진다. 2 ≤ N ≤ 16
- 다음 N개의 줄에는 비용 행렬이 주어진다. 각 행렬의 성분은 1,000,000 이하의 양의 정수이며, 갈 수 없는 경우는 0이 주어진다. W[i][j]는 도시 i에서 j로 가기 위한 비용을 나타낸다.
- 항상 순회할 수 있는 경우만 입력으로 주어진다.
출력
- 첫째 줄에 외판원의 순회에 필요한 최소 비용을 출력한다.
예제
입력
1
2
3
4
5
4
0 10 15 20
5 0 9 10
6 13 0 12
8 8 9 0
출력
1
35
풀이
외판원 순회 문제는 NP-hard 문제로 알려져 있어, 모든 가능한 경우의 수를 확인해야 합니다. N! 가지의 경우의 수가 있으므로 이를 완전탐색으로 해결하기 위해서는 시간복잡도가 매우 큽니다. 하지만 비트마스크와 동적계획법(DP)를 사용하여 이 문제를 해결할 수 있습니다.
파이썬 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
n = int(input())
w = [list(map(int, input().split())) for _ in range(n)]
dp = [[None] * (1 << n) for _ in range(n)]
INF = float('inf')
def find_path(last, visited):
# 모든 도시를 방문했을 때
if visited == (1 << n) - 1:
return w[last][0] or INF
# 이미 계산된 경우
if dp[last][visited] is not None:
return dp[last][visited]
tmp = INF
for city in range(n):
if visited & (1 << city) == 0 and w[last][city] != 0:
tmp = min(tmp, find_path(city, visited | (1 << city)) + w[last][city])
dp[last][visited] = tmp
return tmp
print(find_path(0, 1 << 0))
코드 설명
- 입력을 받아서 도시 수
n
과 비용 행렬w
를 설정합니다. dp
배열을 생성하여 중복 계산을 방지합니다.- 재귀 함수
find_path
는 현재 도시와 이미 방문한 도시의 상태를 인자로 받습니다. - 모든 도시를 방문했을 경우 시작 도시로 돌아가는 비용을 반환하거나, 갈 수 없으면 무한대 값을 반환합니다.
- 이미 계산된 경우 저장된 값을 반환합니다.
- 모든 도시에 대하여 아직 방문하지 않았고, 가는 길이 있는 경우를 확인하며 최소 비용을 계산합니다.
- 계산된 값을
dp
에 저장하고 반환합니다.
마지막으로 find_path(0, 1 << 0)
을 호출하여 문제를 해결합니다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.