λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°

μ•Œκ³ λ¦¬μ¦˜ 문제

2098 - μ™ΈνŒμ› 순회

문제: boj.kr/2098
λ‚ μ§œ: 09/16 (μ›”)
성곡여뢀: X

 

μ™ΈνŒμ› 순회(TSP)λŠ” n개의 λ„μ‹œκ°€ 있고 각 λ„μ‹œλ₯Ό μ΄μ–΄μ£ΌλŠ” κΈΈκ³Ό 각 길에 λŒ€ν•œ λΉ„μš©μ΄ μžˆμ„ λ•Œ μ™ΈνŒμ›μ΄ λͺ¨λ“  λ„μ‹œλ₯Ό λŒμ•„ λ‹€μ‹œ μΆœλ°œν•œ λ„μ‹œλ‘œ λŒμ•„μ˜€λŠ” μ΅œμ†Œ λΉ„μš©μ„ κ΅¬ν•˜λŠ” λ¬Έμ œμ΄λ‹€.

 

DP의 λŒ€ν‘œμ μΈ λ¬Έμ œμž„μ€ μ•Œκ³ μžˆμ—ˆμœΌλ‚˜, κ²°κ΅­ μ–΄λ–»κ²Œ ν‘ΈλŠ”μ§€ λͺ°λΌ μ‹€νŒ¨ν–ˆλ‹€.

 

 

μ™„μ „ 탐색

κ°€μž₯ λ¨Όμ € 생각해볼 수 μžˆλŠ” μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€. N이 16이닀.

 

λͺ¨λ“  λ„μ‹œκ°€ μ„œλ‘œ μ΄μ–΄μ Έμžˆλ‹€κ³  κ°€μ •ν•˜κ³  ν•œ λ„μ‹œ 쀑 ν•˜λ‚˜λ₯Ό 골라 μΆœλ°œν•˜μ—¬ κ·Έ λ„μ‹œλ₯Ό μ œμ™Έν•˜κ³  λ‚˜λ¨Έμ§€ λ„μ‹œλ₯Ό κ³ λ₯΄κ³  또 λ‚˜λ¨Έμ§€ λ„μ‹œλ₯Ό κ³ λ₯΄κ³  ... 이λ₯Ό λ°˜λ³΅ν•˜μ—¬ 총 16개 λ„μ‹œλ₯Ό μ „λΆ€ 돌고 λ‹€μ‹œ 처음 λ„μ‹œλ‘œ λŒμ•„μ˜€λŠ” 경우의 μˆ˜λŠ”

16C1 * 15C1 * 14C1 * ... * 1C1 * 1C1(처음 λ„μ‹œλ‘œ λŒμ•„κ°) = 16!이닀.

λ„ˆλ¬΄ν•œ 숫자

 

16!의 값은 ν„°λ¬΄λ‹ˆμ—†μ΄ 큰 κ°’μž„μ„ μœ„μ˜ 사진을 톡해 μ•Œ 수 μžˆλ‹€.. 그럼 이 경우의 수λ₯Ό μ΅œλŒ€ν•œ μ€„μ—¬μ•Όν•œλ‹€. 그러기 μœ„ν•΄μ„œ 경우의 수λ₯Ό 쑰금 λΆ„μ„ν•΄λ³΄μž. 예λ₯Ό λ“€μ–΄ 7개의 λ„μ‹œκ°€ 있고 1μ—μ„œ μΆœλ°œν–ˆλ‹€κ³  ν•˜μž.

 

1 → 2 → 3 → 4 → 5 → 6 → 7 → 1

 

μ²˜μŒμ— ν•΄λ‹Ή 경둜둜 μ™ΈνŒμ›μ΄ 순회λ₯Ό λ§ˆμ³€λ‹€. 그리고 λ‚˜μ„œ λ‹€μŒ 경둜둜 이동을 ν–ˆλ‹€.

 

1 → 2 → 3 → 4 → 7 → 5 → 6 → 1

 

7 → 5 → 6 → 1둜 μ΄λ™ν•œ 것은 μœ„μ˜ κ²½μš°μ™€ λ‹€λ₯΄κ²Œ μ΄λ™ν–ˆμœΌλ‚˜ 이 전에 경둜인 1 → 2 → 3 → 4 이동은 사싀 처음 μ΄λ™ν•œ κ²½μš°μ™€ κ²ΉμΉœλ‹€! κ·ΈλŸΌμ—λ„ λΆˆκ΅¬ν•˜κ³  ν•œλ²ˆ 더 λ°©λ¬Έν•˜μ—¬ 괜히 νž˜μ„ λ‚­λΉ„ν•˜κ³  μžˆλ‹€. 

 

μ™ΈνŒμ›μ΄ 처음 경둜둜 순회λ₯Ό ν•˜κ³ μ„œ 지도에 1 → 2 → 3 → 4둜 κ°€λŠ” λΉ„μš©μ˜ 총합을 적어두고 두 번째 경둜둜 이동할 λ•Œ 1 → 2 → 3 → 4 κ²½λ‘œλŠ” μŠ€ν‚΅ν•˜κ³  4번째 λ„μ‹œλΆ€ν„° 7 → 5 → 6 → 1 경둜둜만 μ΄λ™ν•˜λ©΄ νž˜μ„ μ•„λ‚„ 수 μžˆμ„ν…λ° 말이닀.

 

λ°”λ‘œ μ΄λ•Œ λ¬Έμ œν’€μ΄μ— DP의 λ©”λͺ¨μ΄μ œμ΄μ…˜μ„ μ‚¬μš©ν•œλ‹€λŠ” 힌트λ₯Ό 얻을 수 μžˆλ‹€. κ½€ 큰 힌트λ₯Ό μ–»μ—ˆλ‹€! 

 

 

DP둜 μ ‘κ·Όν•˜μž!

그럼, 이제 각 경둜의 μˆœμ„œλ³„λ‘œ λΉ„μš©μ˜ 총합을 μ €μž₯해두고 ν™œμš©ν•˜λ©΄ μ€‘λ³΅λ˜λŠ” κ²½λ‘œκ°€ κ½€ μ—†μ–΄μ§€λ―€λ‘œ 경우의 μˆ˜κ°€ 많이 쀄어듀 것 κ°™λ‹€. ν•˜μ§€λ§Œ, κΌ­ 경둜의 μˆœμ„œλ³„λ‘œ λΉ„μš©μ˜ 총합을 μ €μž₯ν•΄μ•Όν• κΉŒ?

 

이λ₯Ό μ•Œμ•„λ³΄κΈ° μœ„ν•΄ 1 → 2 → 3 → 4 → 5 → 6 → 7 → 1 경둜λ₯Ό μ’€ 더 λΆ„μ„ν•΄λ³΄μž. 사싀 이 κ²½λ‘œλŠ” 2 → 3 → 4 → 5 → 6 → 7 → 1 → 2 κ²½λ‘œμ™€ 총합이 κ°™κ³  3 → 4 → 5 → 6 → 7 → 1 → 2 → 3 κ²½λ‘œμ™€ 총합이 κ°™λ‹€.

 

즉,

1 → 2 → 3 → 4 → 5 → 6 → 7 → 1 == 2 → 3 → 4 → 5 → 6 → 7 → 1 → 2 
== 3 → 4 → 5 → 6 → 7 → 1 → 2 → 3 == ... 
== 7 → 1 → 2 → 3 → 4 → 5 → 6 → 7

 

이닀! 이 말은 즉, λ‚΄κ°€ μ–΄λ–€ λ„μ‹œμ—μ„œ μΆœλ°œμ„ ν•˜λ“  κ²°κ΅­ μ΅œμ†Œ λΉ„μš©μœΌλ‘œ κ°€λŠ” κ²½λ‘œλŠ” λͺ¨λ‘ 같은 κ²½λ‘œλΌλŠ” 점이닀.

 

이런 생각을 κ°–κ³  μ•„λž˜ 두 경우의 수λ₯Ό 보자.

1 → 2 → 3 → 4 → 7 → 5 → 6 → 1 

2 → 3 → 4 → 7 → 5 → 6 → 1 → 2

 

두 번째 κ²½μš°μ—μ„œ λ§Œμ•½ λ‚΄κ°€ 4 λ„μ‹œμ— μžˆμ„ λ•Œ 1, 2, 3 λ„μ‹œλ₯Ό 거쳐왔닀고 ν•΄λ³΄μž. 그럼 2 → 3 → 4 → ... → 1 → 2 κ²½λ‘œλΌμ„œ μˆœμ„œκ°€ μƒκ΄€μžˆμ–΄ λ³΄μ΄μ§€λ§Œ κ·Έλƒ₯ 1λΆ€ν„° μΆœλ°œν–ˆλ‹€κ³  보면 1 → 2 → 3 → 4 μ΄λ―€λ‘œ 뒀에 7 → 5 → 6에 λŒ€ν•œ μ΄ν•©λ§Œ ꡬ해주고 1 λ„μ‹œλ‘œ λŒμ•„κ°€λ©΄ 첫 κ²½μš°μ™€ κ°™λ‹€!!! 즉, λ‚΄κ°€ λ°©λ¬Έν•œ 것이 μ΅œμ†Œ λΉ„μš©μ˜ 경둜라면 방문의 μˆœμ„œκ°€ 상관이 μ—†λ‹€λŠ” λœ»μ΄λ‹€.

 

그럼, λ‚˜λŠ” 이 경둜λ₯Ό μ €μž₯ν•  λ•Œ 방문의 μˆœμ„œ 상관없이 κ·Έμ € μ–΄λ–€ λ„μ‹œλ₯Ό λ°©λ¬Έν–ˆλŠ”μ§€λ§Œ ν‘œμ‹œν•˜λ©΄ λœλ‹€! 이λ₯Ό ν‘œμ‹œν•˜κΈ° μœ„ν•΄μ„œ visited λ°°μ—΄, μ§‘ν•© λ“± μ—¬λŸ¬κ°€μ§€κ°€ μžˆκ² μ§€λ§Œ μ—¬κΈ°μ„œλŠ” μ—°μ‚°μ˜ 속도λ₯Ό λ”λ”μš± 쀄여보기 μœ„ν•΄ λΉ„νŠΈλ§ˆμŠ€ν‚Ή κΈ°μˆ μ„ μ‚¬μš©ν•œλ‹€. 총 16개의 λ„μ‹œμ΄λ―€λ‘œ 16개의 λΉ„νŠΈμ— 각각 1, 2, 3, 4, 5, 6, ..., 16 λ„μ‹œλ‘œ 보고 0 λ˜λŠ” 1둜 λ°©λ¬Έ ν‘œμ‹œλ₯Ό ν•˜λŠ” 것이닀.

 

그럼 λ§ˆμ§€λ§‰μœΌλ‘œ 남은 μ˜λ¬Έμ€ DP에 μ–΄λ–»κ²Œ μ €μž₯ν•˜λŠ”κ°€μ΄λ‹€. μ—¬κΈ°μ„œ μ’€ μ΄ν•΄ν•˜λŠ”λ° μ• λ₯Ό λ¨Ήμ—ˆμ§€λ§Œ μœ„μ˜ μ„€λͺ…을 잘 λ³΄μ•˜λ‹€λ©΄ 이해할 수 μžˆμ„ 것이닀. ν˜„μž¬ μœ„μΉ˜μ—μ„œ μ§€λ‚˜μ˜¨ λ„μ‹œλ“€μ— λŒ€ν•œ 총합을 DP에 μ €μž₯ν•΄μ•Όν•˜λ‹ˆκΉŒ μœ„μ˜ 경우 4μ—μ„œ 1 → 2 → 3 → 4κΉŒμ§€μ˜ 합을 DP에 μ €μž₯ν•œλ‹€κ³  ν•˜λ©΄ dp[4][00...1111] = sum; μ΄ 됨을 μ•Œ 수 μžˆλ‹€. 그럼 1차원 λ°°μ—΄μ—λŠ” λ„μ‹œμ˜ 개수 n, 2차원 λ°°μ—΄μ—λŠ” 2^n-1 만큼의 크기가 ν•„μš”ν•¨μ„ μ•Œ 수 μžˆλ‹€. 즉, DP[n][2^n-1] μœΌλ‘œ DP 배열을 μ„ μ–Έν•œλ‹€.

 

μœ„μ˜ 사싀을 λͺ¨λ‘ μ΄ν•΄ν•˜λ©΄ μ–΄λŠμ •λ„ μ‹œν–‰μ°©μ˜€ 끝에 μ•„λž˜ μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μžˆμ„ 것이닀.

 

 

μ½”λ“œ

import java.io.*;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {

    static int n;
    static int[][] w;
    static int[][] dp;
    // Math.min μ‚¬μš©μ„ μœ„ν•΄ Integer.MAX_VALUE;둜 μ΄ˆκΈ°ν™”ν•˜λ©΄ μ•ˆλœλ‹€.
    // if (visited == (1 << n) - 1) ν•΄λ‹Ή μ‘°κ±΄μ—μ„œ 갈 수 μ—†λ‹€λ©΄ μ΅œλŒ“κ°’μ„ μ£Όμ–΄ ν•΄λ‹Ή 값이 μ΅œμ†Œκ°’μœΌλ‘œ κ°±μ‹ λ˜μ§€ μ•Šλ„λ‘ ν•˜λŠ”λ°,
    // λ¦¬ν„΄λœ Integer.MAX_VALUE 값이 int cost = tsp(i, visited | (1 << i)) + w[here][i]; μ—¬κΈ°μ„œ 더해지기 λ•Œλ¬Έμ— Overflowκ°€ λ°œμƒν•œλ‹€.
    // κ·Έλž˜μ„œ, λ‚˜λŠ” 16개의 λ„μ‹œλ₯Ό λΉ™ 돌면 총 17개의 λ„μ‹œλ₯Ό λ°©λ¬Έν•˜λŠ”λ° 각 길의 λΉ„μš© μ΅œλŒ€κ°’μ΄ 100λ§Œμ΄λ―€λ‘œ 1700만 + 1을 μ΅œλŒ“κ°’μœΌλ‘œ μ„€μ •ν•΄μ£Όμ—ˆλ‹€.
    static int inf = 17000001;

    static int tsp(int here, int visited) {
        if (visited == (1 << n) - 1) return (w[here][0] != 0) ? w[here][0] : inf;
        if (dp[here][visited] != 0) return dp[here][visited];

        dp[here][visited] = inf;
        for (int i = 0; i < n; i++) {
            if ((visited & (1 << i)) != 0) continue;
            if (w[here][i] == 0) continue;
            int cost = tsp(i, visited | (1 << i)) + w[here][i];
            dp[here][visited] = Math.min(cost, dp[here][visited]);
        }

        return dp[here][visited];
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine());
        w = new int[n][n];

        StringTokenizer st;
        for (int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < n; j++)
                w[i][j] = Integer.parseInt(st.nextToken());
        }

        dp = new int[n][(1 << n) - 1];
        System.out.println(tsp(0, 1));
    }
}

 

'μ•Œκ³ λ¦¬μ¦˜ 문제' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

2294번 문제 λ©”λͺ¨  (0) 2024.09.22
2240 - μžλ‘λ‚˜λ¬΄  (0) 2024.09.20
2565 - 전깃쀄  (1) 2024.09.12
1561 - 놀이 곡원  (2) 2024.09.09
2156 - 포도주 μ‹œμ‹  (0) 2024.08.30