알고리즘/백준

1028 부분수열의 합2

아메숑 2019. 3. 9. 20:58

1. 문제



2. 코드

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
#include "pch.h"
#include <iostream>
#include <vector>
#include <algorithm>
#define _CRT_SECURE_NO_WARNINGS
using namespace std;
 
//arr의 모든 부분집합의 합을 list에 넣어줌-> 부분집합의 개수 2^arr의사이즈
void makePre(int arr[], int idx, int endint sum, vector<int> &list)
{
    if (idx == end) {
        list.push_back(sum);
        return;
    }
    makePre(arr, idx + 1end, sum, list);
    makePre(arr, idx + 1end, sum + arr[idx], list);
}
 
int main()
{
    int n, sum;
    scanf("%d %d"&n, &sum);
    int arr[50];
    for (int i = 0; i < n; i++) {
        scanf("%d"&arr[i]);
    }
    vector<int> left;
    vector<int> right;
    //arr의 인덱스0부터 n/2까지의 부분집합의 합을 left에 넣음
    makePre(arr, 0, n / 20, left);    
    //arr의 인덱스n/2부터 n/까지의 부분집합의 합을 right에 넣음
    makePre(arr, n / 2, n, 0, right);
 
    //오름차순 정렬
    sort(left.begin(), left.end());
    sort(right.begin(), right.end());
        
    long ans = 0;
    int L = 0;
    int R = right.size()-1;
 
    while (L < left.size() && R >= 0)
    {
        long valL = left[L];
        long valR = right[R];
        //만약 sum이 6이고 left=2,2,4이고 right=5,6,6이면 
        //6을 만드는 경우의수는 2*2=4이다. 이 경우의 수를 ans에 넣어준다.
        if (valL + valR == sum)
        {
            long lc = 0;
            while (L < left.size() && left[L] == valL)
            {
                lc++;
                L++;
            }
            long rc = 0;
            while (R >= 0 && right[R] == valR)
            {
                rc++;
                R--;
            }
            ans += (rc*lc);
        }
        else if (valL + valR > sum) {
            R--;
        }
        else
            L++;
    }
    //부분집합을 만들 때 left,right마다 0값이 들어가므로 
    if (sum == 0)
        ans--;
    printf("%ld", ans);
 
    
}
 
cs


3. 후기

이 문제는 n의 길이가 40까지 주어진다. 그럼 부분집합의 경우의 수는 2^40이고 부분수열의 합1처럼 풀면 시간초과가 난다. 그래서 받은 배열을 2개로 나누어서 경우의 수를 2^20까지 줄일 수 있다. 

arr = {-7,-3,-2,5,8}이라면 배열을 두개로 나누어서 모든 부분집합의 합을 각각의 벡터에 넣고, 정렬한다. 

left = {-10,-7,-3,0}

right = {-2,0,3,5,6,8,11,13}

left는 -10부터 right는 13부터 left와 right를 더하면서 sum을 찾으면 ans를 증가시킨다.