백트래킹 + dp로 풀었다.



왼쪽

1. 모든 투자가가 살아있는 상태에서 시작. 상태는 bitmask로 넘긴다

2. 파산 가능한 투자가를 하나씩 파산시켜보면서 dfs를 돈다.

3. depth == n/2까지 도달하면 dp table에 메모이제이션 하고 리턴한다.


오른쪽

1. i번째 투자가만 살아있는 상태에서 시작. 상태는 bitmask로 넘긴다

2. 현재 투자자 이전에 살아있었을 가능성이 있는 투자가를 살려보면서 dp를 돈다.

3. depth == n-n/2까지 도달하면 dp table을 보고 가능한지 여부를 판단한다.



파산 가능성을 볼 때 부채합산만 보면 바로바로 판단이 가능하다.


부채합산 s[] 를 계산하는법


n == 5, bitmask = 01100 이라면

d[0][0] d[0][1] d[0][2] d[0][3] d[0][4]   : s'[0] = s[0] - d[0][1] - d[0][2]

d[1][0] d[1][1] d[1][2] d[1][3] d[1][4]   : s'[1] = s[1] - d[1][1] - d[1][2]

d[2][0] d[2][1] d[2][2] d[2][3] d[2][4]   : s'[2] = s[2] - d[2][1] - d[2][2]

d[3][0] d[3][1] d[3][2] d[3][3] d[3][4]   : s'[3] = s[3] - d[3][1] - d[3][2]

d[4][0] d[4][1] d[4][2] d[4][3] d[4][4]   : s'[4] = s[4] - d[4][1] - d[4][2]


인 상태이고 3번 투자가가 파산이 가능하다면 추가로 d[i][3]을 s'[i]에서 빼주면 된다.


그리고 bitmask를 참조하여 현재 살아있고 s[i] > 0 이면 파산 가능하므로 백트래킹을 타고 들어가면 된다.


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
79
80
81
82
83
84
85
86
87
#include <iostream>
#include <algorithm>
#include <memory.h>
using namespace std;
typedef long long ll;
typedef pair<intint> pii;
 
int t, tc, n;
int a[20][20], cnt[20], p;
ll s[20];
char d[1 << 20];
 
// bm = 살아남은 목록
char dfs1(int bm, int cnt) {
    if (cnt == n / 2)
        return d[bm] = 1;
    if (d[bm] != -1)
        return d[bm];
    d[bm] = 0;
    for (int i = 0; i < n; ++i) {
        if (!(bm&(1 << i)) || s[i] <= 0 || d[bm ^ (1 << i)] != -1continue;
 
        for (int j = 0; j < n; ++j) s[j] -= a[j][i];
 
        if (dfs1(bm ^ (1 << i), cnt + 1== 1)
            d[bm] = 1;
        for (int j = 0; j < n; ++j) s[j] += a[j][i];
    }
    return d[bm];
}
 
// bm = 살아남은 목록
char dfs2(int bm, int cnt) {
    char &ret = d[bm];
    if (cnt == n - n / 2 || ret != -1)
        return ret;
    ret = 0;
    for (int i = 0; i < n; ++i) {
        if (bm&(1 << i) || s[i] <= 0 || d[bm ^ (1 << i)] == 0continue;
        if (d[bm ^ (1 << i)] == 1
            return ret = 1;
 
        for (int j = 0; j < n; ++j) s[j] += a[j][i];
 
        if (dfs2(bm ^ (1 << i), cnt + 1== 1) {
            for (int j = 0; j < n; ++j) s[j] -= a[j][i];
            return ret = 1;
        }
        for (int j = 0; j < n; ++j) s[j] -= a[j][i];
    }
    return ret;
}
 
int main(void) {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> t;
    for (tc = 1; tc <= t; ++tc) {
        
        memset(s, 0sizeof(s));
        cin >> n;
        memset(d, -1sizeof(char)*(1 << n));
        for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) cin >> a[i][j], s[i] += a[i][j];
 
 
        p = (1 << n) - 1;
        dfs1(p, 0);
 
        for (int i = 0; i < n; ++i) s[i] = 0;
 
 
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) s[j] += a[j][i];
            cnt[i] = dfs2((1 << i), 1);
            for (int j = 0; j < n; ++j) s[j] -= a[j][i];
            if (cnt[i] > 0)
                ans++;
        }
        cout << "#" << tc << " " << ans << " ";
        for (int i = 0; i < n; ++i) {
            if (cnt[i] > 0cout << i + 1 << " ";
            cnt[i] = 0;
        }
        cout << '\n';
    }
    return 0;
}
cs


'SWEA' 카테고리의 다른 글

5548. 태양이의 캠프 조합  (0) 2018.10.16
5789. 현주의 상자 바꾸기  (0) 2018.10.16
1824. 혁진이의 프로그램 검증  (0) 2018.10.08
2112. 보호 필름  (0) 2018.10.08
4740. 밍이의 블록게임  (0) 2018.10.07

+ Recent posts