2020-1ํ•™๊ธฐ, ๋Œ€ํ•™์—์„œ โ€˜์•Œ๊ณ ๋ฆฌ์ฆ˜โ€™ ์ˆ˜์—…์„ ๋“ฃ๊ณ  ๊ณต๋ถ€ํ•œ ๋ฐ”๋ฅผ ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค. ์ง€์ ์€ ์–ธ์ œ๋‚˜ ํ™˜์˜์ž…๋‹ˆ๋‹ค :)

8 minute read

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)๋ฒˆ ์งˆ๋ฌธ์„ ๋งŒ๋“  ๊ฒƒ์ด๋‹ค.

Problem Solving

ํ•จ๊ป˜๋ณด๊ธฐ


reference

Categories:

Updated: