Skils/Algorithm
알고리즘[C++]- Backtracking을 이용한 부분집합의 합(subset_sum)순서쌍 구하기.
재한
2022. 5. 15. 15:03
문제
n개의 원소를 가진 정수의 집합 S가 주어지고,
임의의 정수 W가 주어졌을 때,
합이 W가 되는 부분집합의 개수와 각 부분집합을 출력하도록 하시오.
입력
첫 줄에 원소의 개수 n과 임의의 정수 W가 주어진다.
둘째 줄에 n개의 정수가 주어진다.
출력
첫 줄에 원소의 합이 W가 되는 부분집합의 개수를 출력한다.
둘째 줄부터 원소의 합이 W가 되는 모든 부분집합을 한 줄에 하나씩 출력한다.
단, 부분집합에서의 원소 출력 순서는 주어진 S의 원소 순서와 동일해야 한다. (백트래킹 순서대로)
※처음 문제를 보고 떠오른 생각
- 주어진 집합 S의 원소들을 더해서 W를 만들어야 한다.
- Backtracking을 이용해서 모든 경우의 수를 구한다.
- 유망성검사를 통해서 가지치기를 함.
- 만약 뽑은 원소들의 합이 W면 종료.
ex) 만약 원소의 개수가 4개이고, W가 13이고 집합 S의 원소가 {3,4,5,6} 일 때 S의 원소들로 13을 만들 수 있는 경우의 수는 {3,4,6} 일 것이다. (3+4+6=13)
설계
- 집합 S의 원소들로 13을 만들 수 있을지 없을지부터 따져봐야 할 것이다. (S의 모든 원소들의 합이 W보다 크거나 같아야 한다.) ->만들 수 없으면 0을 출력하면 된다.
- 각각의 원소들을 더하는 경우와 안 더하는 경우 2가지가 있다. 그걸 코드상으로 include라는 배열을 만들어서 0이면 원소를 안 더했다는 뜻이고 1이면 원소를 더했다는 뜻이다.
- 모든 경우의 수를 방문하는 것을 원칙으로 하지만 효율적으로 방문하기 위해서 유망성 검사를 한다.(코드 상으로는 promising함수이다.)
- 유망성 검사 조건 1)
- 이때까지 더한 값과 다음 더하려는 값의 합이 W값을 초과하면 안 된다. (다음 값을 더할 때 W를 넘으니 굳이 탐색 안 해도 됨.)
- 유망성 검사 조건 2)
- 이때까지 더한 값과 남아 있는 원소의 합(total)이 W보다 작으면 안 된다.(남아있는걸 다 더해도 W가 되지 않으니까 굳이 탐색할 필요가 없음.)
- 유망성 검사 조건 1)
- 유망성 검사를 해서 조건에 부합하면 가지치기를 하고 통과되면 탐색을 실시해서 결과를 구현함.
구현
1. 유망성 검사(pruning)
bool promising(int i, int weight, int total) {
return (weight + total >= W) && (weight == W || weight + sub[i + 1] <= W);
} //유망성 검사를 해서 통과하면 1 리턴 안되면 0리턴 해서 가지치기 실시.
2. 부분집합을 더하는 함수
- weight는 현재 내가 더한 값의 합이고 total은 집합 S의 원소합이다.
- include [i+1]=true는 i+1번째 수를 내가 더해줬다는 뜻이고 false는 더하지 않았다는 뜻이다.
- 더했으면 weight의 값을 더해주고, 원소를 검사했으니 total값에서 그 원소의 값을 빼준다.
- 더하지 않았으면 weight의 값을 더해줄 필요가 없고, 원소를 검사했으니 total값에서 그 원소의 값을 빼준다.
- 종료 조건은 내가 더해준 값의 합이 W가 되면 종료한다.
void subset(int i, int weight, int total) {
if (promising(i, weight, total)) {
if (weight == W)
{
cnt++;
string ab = "";
for (int j = 1; j <= i; j++)
{
if (include[j] == 1)
str.push_back(to_string(sub[j]));
}
str.push_back("");
//ab에 저장된걸 스트링 배열에 넣고싶음
}
else
{
include[i + 1] = true;
subset(i + 1, weight + sub[i + 1], total - sub[i + 1]);
include[i + 1] = false;
subset(i + 1, weight, total - sub[i + 1]);
}
}
}
가지치기의 조건만 잘 설정하면 쉽게 풀 수 있는 문제이다.
include배열을 굳이 써야 했나 싶지만, 문제에서 요구하는 출력을 하기 위해서는 어쩔 수 없는 선택인 것 같다..