HyeLog

백준_1182_부분수열의 합 본문

알고리즘

백준_1182_부분수열의 합

shj718 2022. 4. 14. 14:55

문제: https://www.acmicpc.net/problem/1182

 

1182번: 부분수열의 합

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

www.acmicpc.net

 

알고리즘: 브루트포스-재귀

 

📌 1번째 풀이

✔️로또 문제의 로직을 응용했다. 로또 문제는 선택해야 하는 개수가 6개로 정해져 있었지만, 부분수열의 합 문제는 부분수열의 길이가 정해져 있지 않으므로, for문으로 부분수열의 길이가 1, 2, ··· [수열의 길이] 일때 각각 재귀함수를 호출했다.

 

🚨for문 안에서 아래와 같이 벡터를 선언했더니 벡터에 push_back()이 안되었다.

for (int i = 1; i <= len; i++) {
	vector<int> sub(i);
	solve(arr, i, 0, 0, sub);
}

아래와 같이 길이를 미리 지정하지 않는 코드로 바꿨더니 제대로 동작했다.

for (int i = 1; i <= len; i++) {
	vector<int> sub;
	solve(arr, i, 0, 0, sub);
}

 

코드:

#include <iostream>
#include <vector>

using namespace std;

int len, sum, ans = 0;

void solve(vector<int>& arr, int subLen, int cnt, int index, vector<int>& sub) {
	// 정답 찾은 경우
	if (cnt == subLen) {
		// 합 검사
		int result = 0;
		for (int i = 0; i < subLen; i++) {
			result += sub[i];
		}
		if (result == sum) ans++;
		return;
	}
	// 불가능한 경우
	if (index == len) return;
	// 다음 호출
	// index번째 선택 O
	sub.push_back(arr[index]);
	solve(arr, subLen, cnt + 1, index + 1, sub);
	sub.pop_back();
	// index번째 선택 X
	solve(arr, subLen, cnt, index + 1, sub);
}

int main() {
	cin >> len >> sum;
	vector<int> arr(len);
	for (int i = 0; i < len; i++) {
		cin >> arr[i];
	}
	for (int i = 1; i <= len; i++) {
		vector<int> sub;
		solve(arr, i, 0, 0, sub);
	}
	cout << ans << '\n';
	return 0;
}

 

📌 2번째 풀이 (Better!)

✔️ 이 문제에서는 로또 문제처럼 부분수열을 출력할 필요 없이, 합만 구하면 되므로(부분수열의 길이도 정해져 있지 않으므로), 굳이 부분수열을 저장할 배열을 만들 필요가 없다.

⭐ 주의해야 할 점: 합이 '0'이 되는 부분수열의 개수를 출력하는 문제라면 → 재귀함수에서 부분수열의 길이가 0인 경우도 세지므로 마지막에 답에서 1을 빼야함.

 

+ 또한, 부분수열을 어떤 순서대로 출력해야하는 문제가 아니므로, 재귀함수의 '다음 호출' 부분에서 index번째를 선택하는 경우와 선택 안하는 경우의 순서가 바뀌어도 상관 없음.

 

코드:

#include <iostream>
#include <vector>

using namespace std;

int len, sum, ans = 0;

void solve(vector<int>& arr, int subSum, int index) {
	// 반드시 모든 원소에 대해 선택했을 때 합 검사
	if (index == len) {
		// 합 검사
		if (subSum == sum) {
			ans++;
		}
		return;
	}
	// 다음 호출
	// index번째 선택 O
	solve(arr, subSum + arr[index], index + 1);
	// index번째 선택 X
	solve(arr, subSum, index + 1);
}

int main() {
	cin >> len >> sum;
	vector<int> arr(len);
	for (int i = 0; i < len; i++) {
		cin >> arr[i];
	}
	solve(arr, 0, 0);
	if (sum == 0) {
		ans--;
	}
	cout << ans << '\n';
	return 0;
}

 

위 코드의 solve 함수를 void형이 아닌 int형을 반환하는 함수로 바꾼 코드는 아래와 같다.

#include <iostream>
#include <vector>

using namespace std;

int N, M;

int solve(vector<int>& v, int idx, int sum) {
	int ans = 0;
	// 성공한 경우
	if (idx == N) {
		if (sum == M) {
			return 1;
		}
		return 0;
	}
	// 실패한 경우 X
	// 다음 호출
	ans += solve(v, idx + 1, sum + v[idx]);
	ans += solve(v, idx + 1, sum);
	return ans;
}

int main() {
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> N >> M;
	vector<int> v(N);
	for (int i = 0; i < N; i++) {
		cin >> v[i];
	}
	// M이 0이면, 아무것도 더하지 않는 경우도 포함되므로 -1 하기
	if (M == 0) {
		cout << solve(v, 0, 0) - 1 << '\n';
	}
	else {
		cout << solve(v, 0, 0) << '\n';
	}
	return 0;
}

 

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

백준_14888_연산자 끼워넣기 (브루트포스-재귀)  (0) 2022.04.18
백준_14225_부분수열의 합(ver2)  (0) 2022.04.14
백준_6603_로또  (0) 2022.04.12
백준_15662_톱니바퀴2  (0) 2022.03.14
백준_16967_배열 복원하기  (0) 2022.02.21