문제

풀이
이 문제는 방법이 다양한것 같았습니다. 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 |