Notice
Recent Posts
Recent Comments
Link
250x250
«   2025/04   »
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
Archives
Today
Total
관리 메뉴

피너클의 it공부방

백준 1208 부분수열의 합 2 (c++) : 피너클 본문

백준

백준 1208 부분수열의 합 2 (c++) : 피너클

피너클 2022. 8. 6. 14:56
728x90
반응형

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

 

1208번: 부분수열의 합 2

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

www.acmicpc.net

이진탐색 문제다.

현재 n이 40이니 모든 부분수열을 구하면 2 ^ 40 =  1,099,511,627,776만큼의 수열을 구해야한다.

그런 2 ^ 20짜리 부분수열 2개를 구할것이다.

long long n, s;
long long arr[41];
vector<long long> first, second;
vector<long long> ::iterator a, b;

선언한 변수들이다. first와 second에는 부분수열들이 들어갈것이고 a, b는 이진탐색에 사용할것이다.

cin >> n >> s;
for (int i = 0; i < n; i++) cin >> arr[i];
make(0, 0, n / 2);
make(n / 2, 0, n);

값을 입력받고 make함수를 돌려준다

make함수는 (현재위치, 합, 마지막)을 입력받는다.

void make(long long idx, long long sum, long long l) {
	if (idx == l) {
		if (l == n / 2) first.push_back(sum);
		else second.push_back(sum);
		return;
	}
	make(idx + 1, sum + arr[idx], l);
	make(idx + 1, sum, l);
}

만약 idx == l이라면 마지막까지 왔다는 뜻이다.

l이 n/2라면 sum을 first에 넣고 아니며 second에 넣는다.

그후 현재 값을 추가한 make와 추가하지 않은 make를 돌린다.

함수를 전부 돌리면 first와 second에 n의 절반 크기의 부분수열이 들어가게된다.

sort(first.begin(), first.end());
sort(second.begin(), second.end());
long long ans = 0;

이제 두 벡터를 정렬한뒤 ans를 준비한다. ans에는 정답이 들어갈것이다.

for (int i = 0; i < second.size(); i++) {
	a = lower_bound(first.begin(), first.end(), s - second[i]);
	b = upper_bound(first.begin(), first.end(), s - second[i]);
	ans += b - a;
}

second의 크기 만큼 반복한다.

lower_bound는 배열에서 목표값 이상의 값의 위치를 반환하고

upper_bound는 배열에서 목표값 초과의 값의 위치를 반환한다.

arr = [10, 20, 30, 30, 30, 40, 50] 배열이 있다고 가정하고 이 안에서 30이 몇개가 있는지를 확인해햐한다 해보자.

lower_bound(arr, arr + 7, 30)을 하면 30 이상의 값이 위치한 첫번째 값인 2가 리턴되고

upper_bound(arr, arr + 7, 30)을 하면 30 초과의 값이 위치한 첫번째 값인 5가 리턴된다.

이 둘을 빼면 3이 나오고 이것이 배열에서 30의 개수이다.

arr = [10, 20, 30, 30, 30, 40, 50] 배열에서 25를 찾는다면 어떻게 될까

lower_bound(arr, arr + 7, 25)을 하면 25 이상의 값이 위치한 첫번째 값인 2 (arr[2] = 30) 가 리턴되고

upper_bound(arr, arr + 7, 25)을 하면 25 초과의 값이 위치한 첫번째 값인 2 (arr[2] = 30) 가 리턴되

이 둘을 빼며 0이 나오고 이것이 배열에서 25의 개수가 된다.

b - a 는 first배열에서 s-seound[i]가 몇개인지를 반환한다.

if (s == 0) ans -= 1;
cout << ans << endl;

s가 0이면 ans-1을 한다. 크기가 양인 부분수열중 구해야 하는데

아무것도 고르지 않은 부분수열이 포함되어있기 때문이다.

그후 ans를 출력한다.

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

long long n, s;
long long arr[41];
vector<long long> first, second;
vector<long long> ::iterator a, b;

void make(long long idx, long long sum, long long l) {
	if (idx == l) {
		if (l == n / 2) first.push_back(sum);
		else second.push_back(sum);
		return;
	}
	make(idx + 1, sum + arr[idx], l);
	make(idx + 1, sum, l);
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	cin >> n >> s;
	for (int i = 0; i < n; i++) cin >> arr[i];
	make(0, 0, n / 2);
	make(n / 2, 0, n);
	sort(first.begin(), first.end());
	sort(second.begin(), second.end());
	long long ans = 0;
	for (int i = 0; i < second.size(); i++) {
		a = lower_bound(first.begin(), first.end(), s - second[i]);
		b = upper_bound(first.begin(), first.end(), s - second[i]);
		ans += b - a;
	}
	if (s == 0) ans -= 1;
	cout << ans << endl;
}

 

전체코드다.

728x90
반응형
Comments