반응형

https://www.acmicpc.net/problem/1208

 

1208번: 부분수열의 합 2

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

www.acmicpc.net

 

부분수열의 합 1에서는 주어진 정수의 개수 N이 20을 넘지 않기 때문에, 2^20안에 해결할 수 있었습니다.

하지만 이번 문제에서는 주어진 정수의 개수 N이 40을 넘지 않기 때문에, 똑같은 방식으로 해결하려고 하면 2^40의 시간이 걸리며 시간초과가 나는 문제였습니다.

마땅히 해결할 방법이 떠오르지 않아 다른 분들의 풀이를 참고하였습니다.

주어진 예제 입력으로 해결해보겠습니다.

5 0
-7 -3 -2 5 8

1. 먼저 주어진 정수 배열을 두 개로 나눕니다.

배열1 = { -3, -7 };

배열2 = { -2, 5, 8 };

2. 두 개로 나뉜 각 정수 배열에 대하여 모든 부분 수열의 합을 각각 구해줍니다.

3. 두 배열을 정렬해줍니다.

부분수열합1(A배열) = { -10, -7, -3, 0 };

부분수열합2(B배열) = { -2, 0, 3, 5, 6, 8, 11, 13};

4. 각 배열의 index 변수를 i 와 j 라고 할 때, i는 A 배열의 시작점(0), j는 B 배열의 끝점(B.size() - 1)으로 초기화하겠습니다. A 배열은 가장 작은 값부터, B 배열은 가장 큰 값부터 시작하는 것입니다.

A[i] + B[j] < S 이면, 두 배열의 합(부분 수열의 합)을 늘려야합니다. A 배열의 인덱스 i를 1 증가시켜줍니다.

A[i] + B[j] > S 이면, 두 배열의 합(부분 수열의 합)을 줄여야합니다. B 배열의 인덱스 j를 1 감소시켜줍니다.

A[i] + B[j] == S 이면, S와 일치하는 부분 수열의 합의 개수를 구해야합니다.

이 때, A[i], A[i+1], ... 의 값들이 A[i]와 동일하고, B[j], B[j-1], ... 의 값들이 B[j]와 동일하다면,

(S와 일치하는 부분 수열의 합의 개수 = A[i]와 연속해서 동일한 갯수 * B[j]와 연속해서 동일한 갯수) 입니다.

예를 들어,

A = [0, 1, 1, 2]

B = [1, 1, 1, 2] 에서 S == 2, 현재 i = 1, j = 2라고 가정하겠습니다.

A[i] + B[j] == 2 입니다. 부분 수열의 합의 개수를 구해야합니다.

A[i]와 연속해서 동일한 값들은 2개, B[i]와 연속해서 동일한 값들은 3개 입니다.

각 값들에 녹아있는 부분 수열의 합을 조합하여 만들 수 있는 개수는 2*3 = 6개 입니다.

부분 수열의 합의 개수를 구했다면, 이동한 i 와 j 값을 가지고 위 과정을 반복해서 수행해줍니다.

5. 만약, S == 0 이었다면, A와 B 배열에는 0이 중복해서 들어가있으므로, 합의 개수에서 1을 감소시켜줍니다.

 

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int n, s, a[40];
long long int c = 0, c1, c2;
vector<int> l, r;
void f(int idx, int end, int sum, int d) {
    if (idx == end) {
        if (d) l.push_back(sum);
        else r.push_back(sum);
        return;
    }
    f(idx + 1, end, sum, d);
    f(idx + 1, end, sum + a[idx], d);
}
int main() {
    scanf("%d %d", &n, &s);
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    f(0, n / 2, 0, 1);
    f(n / 2, n, 0, 0);

    sort(l.begin(), l.end());
    sort(r.begin(), r.end());

    for (int s1 = 0, s2 = r.size() - 1; s1 < l.size() && s2 >= 0;) {
        if (l[s1] + r[s2] == s) {
            c1 = c2 = 0;
            for (int t = l[s1]; s1 < l.size() && t == l[s1]; s1++, c1++);
            for (int t = r[s2]; s2 >= 0 && t == r[s2]; s2--, c2++);
            c += c1 * c2;
        }
        else if (l[s1] + r[s2] < s) s1++;
        else s2--;
    }
    printf("%lld", s == 0 ? c - 1 : c);
}
반응형

'Algorithm' 카테고리의 다른 글

백준 13460 : 구슬 탈출 2  (0) 2021.11.12
백준 2143 : 두 배열의 합  (0) 2021.11.12
백준 1806 : 부분합  (0) 2021.11.12
백준 2003 : 수들의 합 2  (0) 2021.11.12
백준 14391 : 종이 조각  (0) 2021.11.12

+ Recent posts