알고리즘/BOJ

백준 1208번 부분집합의 합 2

꾸준함. 2019. 1. 27. 19:08

문제 링크입니다: https://www.acmicpc.net/problem/1208


배열을 두개로 나누어서 모든 부분집합의 합을 비트마스킹을 이용하여 미리 전처리한 후 투 포인터 알고리즘을 적용해야했던 어려운 문제였습니다.


알고리즘은 아래와 같습니다.

1. 배열을 두개로 나누고 각각의 배열에 대해 모든 부분집합의 합을 전처리해줍니다.

2. 각각의 배열을 정렬을 하는데 하나는 오름차순 반대는 내림차순으로 정렬하여 투 포인터 알고리즘을 적용합니다.

3. 배열을 두개로 나누었기 때문에 S=0일 경우 답이 하나 더 크게 나옵니다.

-> 각 배열의 부분집합이 공집합을 포함하고 있기 때문입니다. 따라서, S=0일 경우 답을 하나 줄입니다.


#include <iostream>

#include <vector>

#include <algorithm>

#include <functional>

using namespace std;

 

int main(void)

{

        ios_base::sync_with_stdio(0);

        cin.tie(0);

        int N, S;

        cin >> N >> S;

 

        vector<int> v(N);

        for (int i = 0; i < N; i++)

                 cin >> v[i];

 

        //배열을 반으로 나누고

        int half = N / 2;

        //나눈 배열 중 첫 번째 배열의 모든 부분 집합의 합을 저장

        vector<int> first(1 << (N - half));

        for (int i = 0; i < 1 << (N - half); i++)

                 for (int j = 0; j < N - half; j++)

                         if (i&(1 << j))

                                 first[i] += v[j];

        //나머지 배열의 모든 부분 집합의 합을 저장

        vector<int> second(1 << half);

        for (int i = 0; i < 1 << half; i++)

                 for (int j = 0; j < half; j++)

                         if (i&(1 << j))

                                 second[i] += v[j + (N - half)];

 

        //오름차순 정렬

        sort(first.begin(), first.end());

        //내림차순 정렬

        sort(second.begin(), second.end(), greater<int>());

       

        int idx=0, idx2 = 0;

        long long result = 0;

        while (idx < 1 << (N - half) && idx2 < 1 << half)

        {

                 if (first[idx] + second[idx2] == S)

                 {

                         long long cnt = 1, cnt2 = 1;

                         idx++, idx2++;

                         while (idx < 1 << (N - half) && first[idx] == first[idx - 1])

                         {

                                 idx++;

                                 cnt++;

                         }

                         while (idx2 < 1 << half && second[idx2] == second[idx2 - 1])

                         {

                                 idx2++;

                                 cnt2++;

                         }

                         result += cnt * cnt2;

                 }

                 else if (first[idx] + second[idx2] < S)

                         idx++;

                 else if (first[idx] + second[idx2] > S)

                         idx2++;

        }

       

        //배열을 반으로 나누었으므로 공집합(0)이 원래는 하나인데 두번 나옴

        if (S == 0)

                 result--;

        cout << result << "\n";

        return 0;

}


개발환경:Visual Studio 2017


지적, 조언, 질문 환영입니다! 댓글 남겨주세요~

반응형

'알고리즘 > BOJ' 카테고리의 다른 글

백준 3020번 개똥벌레  (8) 2019.01.27
백준 7453번 합이 0인 네 정수  (3) 2019.01.27
백준 1644번 소수의 연속합  (0) 2019.01.27
백준 1806번 부분합  (4) 2019.01.27
백준 2003번 수들의 합 2  (0) 2019.01.27