알고리즘/BOJ 문제풀이

[DP][Bitmask]BOJ 1102_발전소

4Legs 2020. 9. 26. 00:03

문제 링크 : www.acmicpc.net/problem/1102

 

1102번: 발전소

은진이는 발전소에서 근무한다. 은진이가 회사에서 잠깐 잘 때마다, 몇몇 발전소가 고장이난다. 게다가, 지금 은진이의 보스 형택이가 은진이의 사무실로 걸어오고 있다. 만약 은진이가 형택이�

www.acmicpc.net

 

DP와 Bitmask를 활용해 해결할 수 있는 문제이다. Bitmask에 대해서는 다음 링크에 설명해 두었다.

 

4legs-study.tistory.com/5

 

Bitmask 에 대해

[서론] 스위치 4개가 있다고 생각해 보자. 이 스위치가 각각 켜져 있거나, 꺼져 있는 경우를 나타내려면 어떻게 해야 할까? 단순하게 생각해 보면 각 스위치마다 켜져 있는지, 꺼져 있는지에 ��

4legs-study.tistory.com

 

각각의 발전소를 on/off 상태로 나타낼 수 있으므로,

모든 발전소에 대해 특정 발전소가 켜져 있는 전체 상태(State)를 Bitmask로 나타낼 수 있다.

 

특정 State에서 켜져 있는 i번째 발전소와 그 발전소를 통하여

켤 수 있는 모든 j번째 발전소에 대해, 그 발전소를 켰을 때의 newState는 Bitmask를 통해

newState = State | 1 << (i - 1)

과 같이 나타낼 수 있을 것이다. (첫 번째 발전소를 0번이 아닌 1번이라 가정했을 때)

 

이러한 연산을 통해 newState로 이동할 수 있는 최소비용이 곧 dp[newState]가 된다.

 

 

예를 들어 현재 발전소의 State가 Bitmask로 10001일 때, 10111 State로 가는 방법은 두 가지일 것이다.

1. 10001 --(2번째 발전소 on)--> 10011 --(3번째 발전소 on)--> 10111

2. 10001 --(3번째 발전소 on)--> 10101 --(2번째 발전소 on)--> 10111

 

1의 경우, 10111 상태로 가는 비용은 

 min(1->2 비용, 4->2 비용) + min(1->3 비용, 2->3 비용, 4->3 비용) 

 

2의 경우는

 min(1->3 비용, 4->3 비용) + min(1->2 비용, 3->2 비용, 4->2 비용)

 

최종적으로 10001 상태에서 10111 상태로 가는 최소비용은 1, 2 경우의 최소 결과값이 된다.

 

 

[코드]

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
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <algorithm>
 
#define INF 100000000
 
using namespace std;
 
int genval[17][17];
int dp[1<<17];
int turnon, total, goal, oncnt, n;
 
int recursive(int turnon) {
    //ret은 dp[turnon]의 Reference. 즉, ret = dp[turnon]
    int& ret = dp[turnon];
    //dp[turnon]이 새 값으로 채워진 상태라면 그 값을 반환
    if (ret != -1return ret;
    if (oncnt >= goal) return ret = 0;
    ret = INF; oncnt++;
 
    for (int i = 0; i < n; i++) {
        if (turnon & (1 << i)) {
            //켜져 있는 i 번째 발전소에 대해
            for (int j = 0; j < n; j++) {
                if (!(turnon & (1 << j))) {
                    //꺼져있는 j 번째 발전소를 켠다
                    ret = min(ret, recursive(turnon | (1 << j)) + genval[i][j]);
                    //비교 : 현재 저장된 dp[newState]와 i에서 j번째 발전소를 켠 후 최종 비용
                }
            }
        }
    }
    oncnt--;
    return ret;
}
 
int main() {
    fill(dp, dp + (1 << 17), -1);
    string onlist;
 
    cin >> n;
    for (int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            cin >> genval[i][j];
        }
    }
    cin >> onlist;
    for (int i = 0; i < onlist.size(); i++) {
        if (onlist[i] == 'Y') {
            //최초 켜진 상태를 비트마스크로 저장
            turnon |= (1 << i);
            oncnt++;
        }
    }
    cin >> goal;
    int answer = recursive(turnon);
 
    if (answer == INF) cout << -1 << endl;
    else cout << answer << endl;
 
    return 0;
}
 
cs

조금 특이하게도 Bottom-Up 방식이면서 재귀호출을 이용했지만 일반적인 Bottom-Up과 동일하게 작동한다.

'알고리즘 > BOJ 문제풀이' 카테고리의 다른 글

[BFS]BOJ 1726_로봇  (0) 2020.10.06
[DFS]BOJ 16437_양 구출 작전  (0) 2020.10.03
[DP]BOJ 1176_섞기  (0) 2020.10.02
[DP]BOJ 2600_구슬게임  (0) 2020.09.28
[DP] BOJ 1082_방번호  (0) 2020.09.25