문제 설명

문제 링크

http://icpc.me/1208

 

1208번: 부분수열의 합 2

첫째 줄에 정수의 개수를 나타내는 N과 정수 S가 주어진다. (1 ≤ N ≤ 40, |S| ≤ 1,000,000) 둘째 줄에 N개의 정수가 빈 칸을 사이에 두고 주어진다. 주어지는 정수의 절댓값은 100,000을 넘지 않는다.

www.acmicpc.net

풀이

모든 조합을 뽑아서 확인해보면 O(2^40)이기 때문에 시간이 터진다.

그렇기 때문에 반절로 나눠서 모든 조합을 확인해봐야 한다.

x + y = s를 이용하여 오른쪽 조합에서 하나를 뽑아 s - y가 왼쪽 조합에 몇 개 존재하는지 이분 탐색을 이용하여 확인하면 모든 경우를 확인할 수 있다.

이때 몇 개가 존재하는지 확인할 때 upper_bound - lower_bound를 하면 구할 수 있다.

s가 0일 경우엔 왼쪽에서 아무것도 뽑지 않은 경우와 오른쪽도 아무것도 뽑지 않은 경우가 나올 수 있기 때문에 정답에 1을 뺀다.

코드

#include <bits/stdc++.h>
using namespace std;

int arr[42];
vector<int> a, b;

void dfs(int idx, int end, int sum, vector<int> &v) {
    if (idx == end) {
        v.push_back(sum);
        return;
    }
    dfs(idx + 1, end, sum + arr[idx], v);
    dfs(idx + 1, end, sum, v);
}

int main() {
    int n, s; scanf("%d %d", &n, &s);
    for (int i = 0; i < n; i++) scanf("%d", arr + i);
    int mid = n / 2;
    dfs(0, mid + 1, 0, a);
    dfs(mid + 1, n, 0, b);    
    sort(a.begin(), a.end());
    long long ans = 0;
    for (auto k : b) {
        auto u = upper_bound(a.begin(), a.end(), s - k);
        auto l = lower_bound(a.begin(), a.end(), s - k);
        ans += u - l;
    }
    if (!s) ans--;
    printf("%lld", ans);
}

'Algorithm > BOJ' 카테고리의 다른 글

백준[1238] 파티  (0) 2022.01.11
백준[1562] 계단 수  (0) 2022.01.09
백준[6086] 최대 유량  (0) 2021.12.27
백준[1202] 보석 도둑  (0) 2021.11.24
백준[5719] 거의 최단 경로  (0) 2021.11.23
복사했습니다!