문제

풀이
이 문제는 방법이 다양한것 같았습니다. dfs로 푸신분들도 계셨지만 저는 최근에 그리디를 좀 많이 풀었어서 그런지 그리디로 먼저 접근했습니다.
우선 수열이 a1, a2, a3, ... an 까지 존재한다고 했을 때 이 수열의 부분수열의 합을 2^n개 만들 수 있습니다.
하지만 문제에서 주어지는 입력은 그 부분수열의 합인 2^n개의 수가 주어집니다.
따라서 어떤 수열의 조합으로 입력되는 부분 수열의 합을 만들 수 있는지 원래 수열의 원소들을 구하는 문제입니다.
처음 생각해본건 두가지입니다.
1. 입력으로 주어지는 수열 중 0은 절대 답이 될 수 없음
2. 0 다음으로 제일 작은 수는 무조건 원래 수열의 원소에 해당함
1번은 원래 수열의 원소 ai의 범위는 1 ~ 100000입니다. 0을 만들려면 어떤 원소도 더하지 않는 경우밖에 없습니다.
2번은 0 다음으로 제일 작은 수는 어떤 수를 더하더라도 만들 수 없습니다. 그냥 자신만이 그 수를 만들 수 있습니다.
0 다음으로 제일 작은 수가 여러개라면 그 같은 수들을 모두 더해주도록 합니다.
여기서 시작해서 어떻게 그리디하게 접근할 수 있을까 고민하다가 map과 set을 사용하게 되었습니다.
map과 set에는 원래 수열로 인해 제거되는 부분 수열의 값을 저장합니다.
제가 수를 제거한 방식으로는
set에 지금까지 모은 원래 수열의 부분 수열의 합을 모두 다 저장합니다.
map에는 set에서 구한 합들을 카운팅합니다.
예를들어 지금까지 구한 원래 수열이 1, 2, 3라고 했을 때 얘내들의 부분 수열의 합은 0 1 2 3 3 4 5 6 입니다.
그리고 다음 추가되는 원소가 4라고 했을 때 원래 수열은 1, 2, 3, 4가 되고,
이전에서 구했던 부분 수열의 합에서 모두 +4를 해주면 4 5 6 7 7 8 9 10이 되는데,
이걸 부분 수열의 합으로 모두 추가하면 0 1 2 3 3 4 4 5 5 6 6 7 7 8 9 10이 되고, 이는 수열 1, 2, 3, 4의 부분 수열의 합이 됩니다.
위에서 구한 값들을 set과 map에 넣어주고,
다음 들어오는 값을 key로 map에 검색하여 1 이상으로 카운팅되어 있다면 선택하지 않고 넘어가는 방식입니다. 이때 map[key]를 1씩 감소시킵니다.
여기서 map을 사용한 이유는 원래 수열의 원소를 원래 수열의 합으로 만들 수도 있기 때문입니다.
2 4 6이 원래 수열이라면 2 + 4로도 6을 만들 수 있기 때문에 6이 걸러지는 문제가 있었습니다. 덕분에 코드가 더러워졌습니다...
map에서 카운팅한 만큼의 수를 걸러내고, 제거되지 않은 수들 중 제일 작은 수를 순서대로 넣어주면 원래 수열을 구할 수 있습니다.
코드
| 
 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 
 | 
 #include <iostream> 
#include <algorithm> 
#include <vector> 
#include <map> 
#include <set> 
#define MAX 1500001 
using namespace std; 
set<int> s; 
map<int, int> m; 
vector<int> v; 
int list[MAX]; 
int N; 
void eraseNum(int x) { 
    set<int>::iterator iter = s.begin(); 
    vector<int> tmp; 
    for (; iter != s.end(); iter++) { 
        tmp.push_back(*iter + x); 
    } 
    for (auto y : tmp) { 
        m[y]++; 
        s.insert(y); 
    } 
} 
void init() { 
    sort(list, list + (1 << N)); 
    v.push_back(list[1]); 
    m[0]++; 
    m[list[1]]++; 
    s.insert(0); 
    s.insert(list[1]); 
    for (int i = 2; i < (1 << N); i++) { 
        if (list[1] != list[i]) break; 
        v.push_back(list[i]); 
        eraseNum(list[i]); 
    } 
} 
void func() { 
    init(); 
    for (int i = 2; i < (1 << N); i++) { 
        if (m.find(list[i]) != m.end()) { 
            m[list[i]]--; 
            if (!m[list[i]]) m.erase(list[i]); 
            continue; 
        } 
        if (v.size() == N) break; 
        v.push_back(list[i]); 
        eraseNum(list[i]); 
        for (int j = i + 1; j < (1 << N); j++) { 
            if (list[i] != list[j]) break; 
            if (v.size() == N) break; 
            v.push_back(list[j]); 
            eraseNum(list[j]); 
        } 
    } 
    for (auto x : v) { 
        cout << x << ' '; 
    } 
    cout << '\n'; 
} 
void input() { 
    cin >> N; 
    for (int i = 0; i < (1 << N); i++) { 
        cin >> list[i]; 
    } 
} 
int main() { 
    cin.tie(NULL); cout.tie(NULL); 
    ios::sync_with_stdio(false); 
    input(); 
    func(); 
    return 0; 
} 
 | 
cs | 
'대회 > elice' 카테고리의 다른 글
| 엘리스 코드 챌린지 Day 7 계기판 조작하기 (0) | 2024.07.23 | 
|---|---|
| 엘리스 코드 챌린지 Day 6 빨간 선과 파란 선 (0) | 2024.07.23 | 
| 엘리스 코드 챌린지 Day 4 트리 위의 게임 (0) | 2024.07.22 | 
| 엘리스 코드 챌린지 Day 3 문자열 압축 해제 (4) | 2024.07.22 | 
| 엘리스 코드 챌린지 Day 2 정리 정돈을 좋아하는 k씨 (0) | 2024.07.22 | 








