외판원 문제(TSP: Traveling Salesperson Problem)로 대표되는 NP-난해에 해당하는 문제이다. 외판원 문제는 여러 도시가 주어졌을때 외판원이 도시별로 이동하는데 드는 비용이 각각 다른 경우 모든 도시를 다 돌고오는데 드는 비용을 어떻게 최소로 만들 것인지를 물어보는 문제이다.
외판원 문제 - 위키백과, 우리 모두의 백과사전
https://ko.wikipedia.org/wiki/%EC%99%B8%ED%8C%90%EC%9B%90_%EB%AC%B8%EC%A0%9C
발전소 문제도 이와 유사하다. 여러 발전소가 주어지고 발전소가 켜져있는 경우 그 발전소를 이용하여 다른 발전소를 켤 수 있고 그때의 비용은 각각 다르게 주어진다. 이때 최소한 켜져야하는 발전소의 숫자가 주어졌을 때 그 발전소 개수만큼 켜는데 드는 최소비용이 얼마인지를 구하는 문제이다.
기본적으로 이 문제는 동적계획법 문제이다. 처음 주어지는 발전소 조건을 초기값으로 하여 발전소를 하나씩 더 킨다고 했을 때 최소비용을 갱신해가는게 이 문제를 푸는 점화식이 된다. 문제 자체를 푸는 방식은 이해가 가지만 16개의 발전소중 어떤게 켜지고 꺼졌는지를 체크해주는 것이 문제가 된다. 꺼지고 켜지고에 따라서 나올수 있는 가짓수는 2^16이나 되는데 이 모든 케이스를 구분하여 저장하기가 쉽지 않기 때문이다. 이때 사용하는 것이 비트마스크이다. 문제에서 발전소는 켜지고 꺼지는 두 가지 경우밖에 없고 그렇다면 이는 0과 1로 구분해주면 된다. 즉 발전소의 상태를 2진법으로 표시하여 16자리의 2진수를 통해 표시해주는 것이다. 1번 발전소는 2^0의 자리에 작동여부를 표시해주고 2번 발전소는 2^1의 자리에 3번 발전소는 2^2자리에 표시를 해주게 되면 총 2^15 범위내에서 16개 발전소의 작동여부를 표시해줄수 있게 되고 이는 10진수 65536 범위 내에서 모두 표시가 가능해진다.
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 
| 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 
위와 같은 경우라면 자리수에 1을 더하여 계산한다고 했을 때, 1, 3, 4, 11, 16 발전소가 켜져있다는 것을 알 수 있고 2진수로 표시한다고 했을 때, 1000010000001101로 표시된다는 것을 알 수 있다.
발전소 문제에서는 이 비트마스크를 이용하여 나올 수 있는 모든 경우의 수만큼 for문을 돌다가 작동중인 발전소를 발견하면, 그 발전소를 이용하여 작동하지 않고 있는 다른 발전소를 작동시킬 때의 비용을 최소비용으로 업데이트 해주는 방식으로 메모이제이션 해주면서 답을 구해주면 된다.
구현된 소스는 아래와 같다.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.StringTokenizer; public class Main {     private static int[][] dp;     private static final int SIZE = 1 << 16;     private static final int MAX = (int) 1e9 + 1;     private static int P;     private static int N;     private static int[][] E;     private static String onOff;     public static void main(String[] args) throws Exception {         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));         StringTokenizer st = null;         N = Integer.parseInt(br.readLine().trim());         E = new int[N + 1][N + 1];         for (int i = 1; i <= N; i++) {             st = new StringTokenizer(br.readLine().trim());             for (int j = 1; j <= N; j++) {                 E[i][j] = Integer.parseInt(st.nextToken());             }         }         dp = new int[SIZE][2];         for (int i = 0; i < SIZE; i++) {             dp[i][0] = MAX;         }         onOff = br.readLine().trim();         int index = 0;         int cnt = 0;         for (int i = 1; i <= N; i++) {             if (onOff.charAt(N - i) == 'Y') {                 index <<= 1;                 index += 1;                 cnt++;             } else                 index <<= 1;         }         P = Integer.parseInt(br.readLine().trim());         if (P == 0 || cnt >= P) {             System.out.println(0);             return;         }         if (cnt == 0) {             System.out.println(-1);             return;         }         dp[index][0] = 0;         dp[index][1] = cnt;         for (int i = 0; i < (1 << N); i++) {             if (dp[i][0] == MAX)                 continue;             for (int j = 1; j <= N; j++) {                 if ((i & (1 << (j - 1))) != 0) {                     for (int k = 1; k <= N; k++) {                         if ((i & (1 << (k - 1))) == 0) {                             if (dp[i][0] + E[j][k] < dp[i + (1 << (k - 1))][0]) {                                 dp[i + (1 << (k - 1))][0] = dp[i][0] + E[j][k];                                 dp[i + (1 << (k - 1))][1] = dp[i][1] + 1;                             }                         }                     }                 }             }         }         int ans = MAX;         for (int i = 0; i < (1 << N); i++) {             if (dp[i][1] >= P) {                 if (dp[i][0] < ans)                     ans = dp[i][0];             }         }         System.out.println(ans);     } } | cs | 
'Algorithm > Algorithm Study' 카테고리의 다른 글
| 트리의 순회 (0) | 2018.01.13 | 
|---|---|
| LIS (Longest increasing subsequence) nlogn 해법 (0) | 2017.12.31 | 
| 1654번: 랜선 자르기 - Baekjoon Online Judge (0) | 2017.12.16 | 
| 2162번: 선분 그룹 - Baekjoon Online Judge (0) | 2017.11.28 | 
| CCW(CounterClockWise) 알고리즘 (0) | 2017.11.21 |