Traveling Salesman Problem
2020-1ํ๊ธฐ, ๋ํ์์ โ์๊ณ ๋ฆฌ์ฆโ ์์ ์ ๋ฃ๊ณ ๊ณต๋ถํ ๋ฐ๋ฅผ ์ ๋ฆฌํ ๊ธ์ ๋๋ค. ์ง์ ์ ์ธ์ ๋ ํ์์ ๋๋ค :)
<์ธํ์ ์ํ๋ฌธ์ ; Traveling Salesman Problem>์ ๋ํ์ ์ธ non-polynomial problem์ด๋ค. ๋ฌธ์ ๋ ์๋์ ๊ฐ๋ค.
โFind a tour that start and ends at $1$, visits all cities exactly once, and has minimum total length.โ
์ฌ์ค ํญ์ ์์์ ์ผ๋ก ๋์์ค๊ธฐ ๋๋ฌธ์ $1$๋ฒ ๋ ธ๋์์ ์์ํ๋ ๋ค๋ฅธ ๋ ธ๋์์ ์์ํ๋ ์๊ด์ ์๋ค.
์ด ๋ฌธ์ ๋ <์์ ํ์>์ผ๋ก ํธ๋ ์ ๊ทผ๊ณผ <DP>๋ก ํธ๋ ๋ ๊ฐ์ง ๋ฐฉ์์ด ์กด์ฌํ๋ค. ๋น์ฐํ <DP>๋ก ํธ๋๊ฒ <์์ ํ์>๋ณด๋ค ๋ ๋น ๋ฅด์ง๋ง, ๊ตฌํ์ด ์กฐ๊ธ ๋ tricky ํ๋ค.
์์ ํ์
ใ์๊ณ ๋ฆฌ์ฆ ๋ฌธ์ ํด๊ฒฐ์ ๋ตใ์ 1๊ถ์์๋ ์ด ๋ฌธ์ ์ ๋ํ ์์ ํ์ ์ ๊ทผ๋ฒ์ ์ ์ํ๋ค. ์์์๋ถํฐ ๋์๋ฅผ ํ๋์ฉ ์ถ๊ฐํด ๊ฒฝ๋ก(path)๋ฅผ ๋ง๋ค์ด, ๊ฐ๋ฅํ ๋ชจ๋ ๊ฒฝ๋ก๋ฅผ ๋ง๋ค์ด ์ด ์ค์์ ์ต์ ๋น์ฉ์ ์ถ๋ ฅํ๋ค.
์ข ๋ง๋ถ์์ ์ ์๋ ์๊ณ ๋ฆฌ์ฆ์ ๋ฒก์ค 10971๋ฒ: ์ธํ์ ์ํ 2 ๋ฌธ์ ์ ๋ง์ถฐ์ ์์ฑํ๋ฉด ์๋์ ๊ฐ๋ค.
// path: ์ง๊ธ๊น์ง ๋ง๋ ๊ฒฝ๋ก
// visited: ๋์์ ๋ฐฉ๋ฌธ ์ฌ๋ถ
// currentLength: path์ ๊ฒฝ๋ก๋ก ๋ง๋ ๊ธธ์ด
long long shortestPath(vector<int> &path, vector<bool> &visited, long long currentLength) {
// base case: ๋ชจ๋ ๋์๋ฅผ ๋ฐฉ๋ฌธํ๋ค๋ฉด, ์์ ๋์๋ก ๋์๊ฐ๊ณ ์ข
๋ฃ
if (path.size() == N) {
int lastEdge = W[path.back()][path[0]];
return lastEdge ? currentLength + lastEdge : LLONG_MAX;
}
long long ret = LLONG_MAX;
// ๋ฐฉ๋ฌธ ๊ฐ๋ฅํ ๋ชจ๋ ๋์๋ฅผ ๋ฐฉ๋ฌธ
for (int next = 0; next < N; next++) {
if (visited[next]) continue;
int here = path.back();
if (W[here][next] == 0) continue;
path.push_back(next);
visited[next] = true;
long long cand = shortestPath(path, visited, currentLength + W[here][next]);
ret = min(ret, cand);
visited[next] = false;
path.pop_back();
}
return ret;
}
์ ์ฒดํ์์ผ๋ก ๊ฐ๋ฅํ ๋ชจ๋ ๋ฐฉ๋ฌธ ์กฐํฉ์ ๋ชจ๋ ํ์ํ๋ค! ์ฝ๋๋ฅผ ๊ตฌ์ฑํ๋ ๊ณผ์ ์์ โ์กฐํฉโ์ ํ
ํฌ๋์ ์ฌ์ฉํ๋ค. path
, visited
๋ฅผ ์ธ์๋ก ๋๊ฒจ์ผ ํ๋ค๋ ์๊ฐ์ ํ๋๊ฒ ์ด๋ ค์ด ๊ฒ ๊ฐ๋ค ๐ฒ ์ด ์ฝ๋๋ bottom-up ์ฌ๊ท ๋ฐฉ์์ผ๋ก TSP๋ฅผ ๊ตฌํํ๋ค.
๊ทธ๋ฌ๋ <์์ ํ์>์ ์ฑ๋ฅ๋ฉด์์ ์ข์ง ์๋ค. ๊ทธ๋์ ์ด ์ ๊ทผ์ผ๋ก ๋ฐฑ์ค 2098๋ฒ: ์ธํ์ ์ํ๋ฅผ ํ๊ฒ ๋๋ฉด ์๊ฐ ์ด๊ณผ๋ฅผ ๋ฐ๊ฒ ๋๋ค ๐ฅ
Dynamic Programming + Bitmask
<TSP>๋ฅผ <DP>๋ก ํ๊ธฐ ์ํ ์๊ณ ๋ฆฌ์ฆ์ ๋ผ๋๋ ์๋์ ๊ฐ๋ค.
Let $C(j, S)$ the length of shortest path when visit $j$ at last and visit each node in $S$ exactly once.
์ด๋, $C(j, S)$๋ ์๋์ ์์ผ๋ก ๊ตฌํ ์ ์๋ค.
\[C(j, S) = \min_{i \in S, \; i \ne j} \left\{ C(i, S - \{j\}) + d_{ij}\right\}\]๊ทธ๋ผ ์ฐ๋ฆฌ์ ๋ชฉํ๋ $C(i, \{ 1, 2, \dots, n \}) + d_{i1}$ ์ค ๊ฐ์ฅ ์งง์ ๊ฑฐ๋ฆฌ๋ฅผ ๊ตฌํ๋ ๊ฒ์ด๋ค! ์ด๋ฅผ ๊ตฌํ๊ธฐ ์ํ ์๊ณ ๋ฆฌ์ฆ์ ์ฝ๋๋ก ๊ธฐ์ ํ๋ฉด ์๋์ ๊ฐ๋ค. ์ฝ๋๋ โhsp1116โ๋์ ํฌ์คํธ๋ฅผ ์ฐธ์กฐํ๋ค.
TSP์ DP ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํํ๊ธฐ ์ํด์ ๋นํธ๋ง์คํฌ(bitmask) ๊ธฐ๋ฒ์ ๋ํด ์์์ผ ํ๋ค. ํด๋น ํฌ์คํธ๋ฅผ ๋จผ์ ์ดํด๋ณด๊ณ ์ค์.
// curr: ๋ง์ง๋ง์ผ๋ก ๋ฐฉ๋ฌธํ ๋
ธ๋ (=here)
// visited: ๋ฐฉ๋ฌธํ ๋
ธ๋๋ฅผ ๊ธฐ๋กํ ๋นํธ๋ง์คํฌ
long long shortestPath(int curr, int visited) {
// ๋ชจ๋ ๋ฐฉ๋ฌธ
if (visited == ((1 << N) - 1)) {
int lastEdge = W[curr][0];
return lastEdge ? lastEdge : LLONG_MAX;
}
long long ret = LLONG_MAX;
for (int next = 1; next < N; next++) {
if (visited & (1 << next)) continue;
if (W[curr][next] == 0) continue;
long long out = shortestPath(next, visited | (1 << next));
if(out == LLONG_MAX) continue;
ret = min(ret, out + W[curr][next]);
}
return ret;
}
์ฌ์ค ์ฒ์์ ์ ์ํ๋ ์์ ํ์ ์ฝ๋์ ํฌ๊ฒ ๋ค๋ฅด์ง๋ ์๋ค. ๊ทธ๋ฌ๋ ํจ์์ ์ธ์๋ก visited
๋ฅผ ๋นํธ๋ง์คํฌ๋ก ๋ฐ๋๋ค๋ ์ . ๊ทธ๋ฆฌ๊ณ TSP์ ๊ฒฐ๊ณผ๋ฅผ ์ป๊ธฐ ์ํด์ ๊ตฌ์ฒด์ ์ธ ๋ฐฉ๋ฌธ ์์๊ฐ ๋ด๊ธด ๊ฒฝ๋ก path
๊ฐ ํ์์์ผ๋ ๋ง์ง๋ง์ผ๋ก ๋ฐฉ๋ฌธํ ๋
ธ๋์ธ curr
๋ง ๋ฐ๋๋ค๋ ์ ์ด ์ฃผ๋ชฉํ ๋งํ๋ค! ๐คฉ
์ฌ๊ธฐ์ <DP>๋ฅผ ์น์ผ๋ฉด ์๋์ ๊ฐ๋ค.
long long dp[MAX][1 << MAX];
// curr: ๋ง์ง๋ง์ผ๋ก ๋ฐฉ๋ฌธํ ๋
ธ๋ (=here)
// visited: ๋ฐฉ๋ฌธํ ๋
ธ๋๋ฅผ ๊ธฐ๋กํ ๋นํธ๋ง์คํฌ
long long shortestPath(int curr, int visited) {
// ๋ชจ๋ ๋ฐฉ๋ฌธ
if (visited == ((1 << N) - 1)) {
int lastEdge = W[curr][0];
return lastEdge ? lastEdge : LLONG_MAX;
}
if (dp[curr][visited] != 0) return dp[curr][visited];
long long ret = LLONG_MAX;
for (int next = 1; next < N; next++) {
if (visited & (1 << next)) continue;
if (W[curr][next] == 0) continue;
long long out = shortestPath(next, visited | (1 << next));
if(out == LLONG_MAX) continue;
ret = min(ret, out + W[curr][next]);
}
dp[curr][visited] = ret;
return ret;
}
์ฒ์์๋ ๋นํธ๋ง์คํฌ๋ฅผ ์ฐ๊ธฐ ์ํ ๋ฒ์๊ฐ $2^{16}$์ด๋ ๋์ด์ ์ ์ธ ๊ฐ๋ฅํ ๋ฐฐ์ด์ ๋ฒ์๋ฅผ ํจ์ฌ ๋ฒ์ด๋๋ค๊ณ ์๊ฐํ๋๋ฐ, ์์ ์ฝ๋์์๋ dp[MAX][1 << MAX]
์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ํด๊ฒฐํ๋ค. ์ฒ์์๋ ์ด๊ฒ ๋๋ฌธ์ ๋ฐฐ์ด์ด ์๋๋ผ map
ํ์์ผ๋ก DP๋ฅผ ํ ๊น ๊ณ ๋ฏผ๋ ๋ง์ด ํ๋ค.
๋, ์์ง ๋นํธ๋ง์คํฌ์ ๊ธฐ๋ณธ์ด ๋๋ ํ ํฌ๋๋ค์ด ์ต์ํ์ง ์์์ ๋จผ์ ๋นํธ๋ง์คํฌ ๋ฌธ์ ๋ฅผ ์ข๋ ํ๊ณ ์ค๋ฆฌ์ง๋ ๊ธฐ์ ๋ก ์ต๋ํ๋๊ฒ ํ์ํด๋ณด์ธ๋ค.
<TSP>๋ ์๋ ์ ๋ช ํ๊ณ ์ค์ํ ๋ฌธ์ ๋ผ์ ํ์ด๋ดค๋๋ฐ, ๋ง์ ์๊ฐ์ ์ป์ ๊ฒ ๊ฐ๋ค ใ ใ
์๊ฐ ๋ณต์ก๋
DP ์๊ณ ๋ฆฌ์ฆ์ $O(n^2 2^n)$์ ํจ์จ์ ๋ณด์ธ๋ค๊ณ ํ๋ค. ์ด๊ฑด Brute Force์ $O((n-1)!)$ ๋ณด๋ค ์ฝ๊ฐ ์ข์ ์์ค์ด๋ฉฐ DP๋ฅผ ์ผ์์๋ ์ฌ์ ํ exponential time algorithm์ธ ์ํฉ์ด๋ค.
TSP (Search Problem)
TSP ๋ฌธ์ ์ budget $b$๋ฅผ ๋์ ํ์ฌ Search Problem์ ํํ๋ก ๋ฐ๊พธ๊ณ ์ ํ๋ค.
โFind a tour that start and ends at $1$, visits all cities exactly once with total cost $b$ or less.
If thereโs no such tour, then report โno tourโ.โ
์ฐ๋ฆฌ๊ฐ ๋งจ ์ฒ์ ์ดํด๋ดค๋ TSP ๋ฌธ์ ๋ ์ต์ ํด๋ฅผ ์ฐพ๋ optimization problem์ด์๋ค. ๊ทธ๋ฐ๋ฐ ์ด๋ฒ์ ์ ์ํ TSP๋ search problem์ด๋ค. TSP(optimization)๊ณผ TSP(search)๋ฅผ ์๋ก์ ๋ฌธ์ ๋ก ํ์ ๋ ์ ์๋ค.
- TSP(optimization) ์๊ณ ๋ฆฌ์ฆ์ ํตํด TSP(search) ๋ฌธ์ ์ ๋ตํ ์ ์๋ค.
- TSP(search) ์๊ณ ๋ฆฌ์ฆ์ ํตํด tour๊ฐ ์กด์ฌํ๋ ์ง์ ๊น์ง budget $b$๋ฅผ binary search ํ๋ฉด TSP(optimization) ๋ฌธ์ ์ ๋ตํ ์ ์๋ค.
๊ทธ๋ ๋ด ์ ๊ตณ์ด TSP(search)๋ฅผ ์ ์ํ ๊ฑธ๊น? ๊ทธ๊ฒ์ TSP ๋ฌธ์ ์ solution $T$์ correctness๋ฅผ ๊ฒ์ฆํ๊ธฐ ์ํด์๋ค. TSP ๋ฌธ์ ๋ฅผ ๊ฒ์ฆํ๊ธฐ ์ํด์ (1) $T$๊ฐ tour์ธ๊ฐ? (2) $T$์ total length๊ฐ budget $b$ ์ดํ์ธ๊ฐ?๋ฅผ ๊ฒ์ฆํด์ผ ํ๋๋ฐ, TSP(optimization)์ solution $T$๊ฐ ์ฃผ์ด์ก์ ๋ $T$์ total length๊ฐ ์ ๋ง optimal์ธ์ง ๋ตํ๊ธฐ ์ด๋ ต๊ธฐ ๋๋ฌธ์ budget $b$๋ฅผ ํตํ (2)๋ฒ ์ง๋ฌธ์ ๋ง๋ ๊ฒ์ด๋ค.