Link
https://www.acmicpc.net/problem/2087
Detail
단순히 비트마스킹을 사용해서 개의 경우의 수를 통으로 돌리는건 문제가 된다.
따라서 영역을 두 가지로 나눠서 가지의 합을 두 번 구하고,
이 두 값을 Meet-in-the-Middle
로 합쳐서 정답 비트를 찾아내는 방식으로 해결할 수 있다.
Code
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n, K;
vector<int> a;
// 주어진 배열의 모든 부분집합 합을 구하는 함수
vector<pair<int, int>> getSubsetSums(const vector<int>& arr) {
int m = arr.size();
vector<pair<int, int>> sums; // (부분합, 비트마스크) 저장
for (int mask = 0; mask < (1 << m); mask++) {
int sum = 0;
for (int i = 0; i < m; i++) {
if (mask & (1 << i)) sum += arr[i];
}
sums.emplace_back(sum, mask);
}
return sums;
}
// 비트마스크를 n-bit 문자열로 변환하는 함수
string maskToBitString(int mask, int size) {
string bitStr = "";
for (int i = 0; i < size; i++) {
bitStr += (mask & (1 << i)) ? '1' : '0';
}
return bitStr;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
// 입력 처리
cin >> n;
a.resize(n);
for (int i = 0; i < n; i++) cin >> a[i];
cin >> K;
// 절반으로 나누기
int mid = n / 2;
vector<int> left_part(a.begin(), a.begin() + mid);
vector<int> right_part(a.begin() + mid, a.end());
// 왼쪽, 오른쪽 부분합 계산
vector<pair<int, int>> left_sums = getSubsetSums(left_part);
vector<pair<int, int>> right_sums = getSubsetSums(right_part);
// 오른쪽 부분합 정렬 (이진 탐색을 위해)
sort(right_sums.begin(), right_sums.end());
// Meet-in-the-Middle: 왼쪽 부분합을 순회하며 오른쪽에서 보완할 값 탐색
for (auto& p : left_sums) {
int left_sum = p.first;
int left_mask = p.second;
int target = K - left_sum;
// 이진 탐색으로 target 찾기
auto it = lower_bound(
right_sums.begin(),
right_sums.end(),
make_pair(target, 0),
[](const pair<int, int>& a, const pair<int, int>& b) { return a.first < b.first;
}
);
if (it != right_sums.end() && it->first == target) {
int right_mask = it->second;
cout <<
maskToBitString(left_mask, mid) +
maskToBitString(right_mask, n - mid)
<< "\n";
return 0;
}
}
return 0;
}