완전탐색 문제를 풀기위해 필수적으로 알아야되는 순열, 조합, 부분집합, 다음순열의 개념과 간략화한 알고리즘을 정리하려고 한다.
순열, 조합, 부분집합, 다음순열에서 공통적으로 사용할 변수와 의미는 다음과 같다.
int N
: 선택의 후보가 될 수 있는 N개의 수
int M
: 선택하고자 하는 원소의 수
int cnt
: 재귀 호출 내에서 해당 시점에서 선택한 원소의 수를 의미
boolean[] isSelected
: 재귀 수행 중 과거에 해당 원소를 이미 선택을 했는지 판단하기 위한 boolean 배열
int[] selectedArr
: 선택한 원소를 저장할 int 배열
순열은 주어진 숫자들의 순서를 고려하여 조합을 만들기 때문에 아래 두 가지를 체크해야한다.
// 1 ~ N 사이의 값 중에 M개 뽑기
public void permutation(int cnt) {
if (cnt == M) {
// 순열을 완성했을 때 처리할 로직
return;
}
for (int i = 0; i < N; i++) {
if (isSelected[i]) continue;
selectedArr[cnt] = i + 1;
isSelected[i] = true;
permutation(cnt + 1);
isSelected[i] = false;
}
}
조합은 주어진 숫자들의 순서를 고려하지 않고 조합을 만들기 때문에 순열처럼 선택 여부를 확인할 필요가 없다. 조합을 함수를 호출 할 때 현 시점에서 선택한 인덱스 + 1의 값을 매개변수로 넘겨주어 항상 다음 수 부터 선택하도록 구현할 수 있다.
// 1 ~ N 사이의 값 중에 M개 뽑기
public void combination(int cnt, int index) {
if (cnt == M) {
// 조합을 완성했을 때 처리할 로직
return;
}
for (int i = index; i < N; i++) {
selectedArr[cnt] = i + 1;
combination(cnt + 1, i + 1);
}
}
집합의 원소들을 선택, 비선택하는 과정을 통해 공집합을 포함한 각기 다른 부분집합을 구할 수 있다.
// 1 ~ N 사이의 값 중에 M개 뽑기
public void subset(int cnt) {
if (cnt == M) {
for (int i = 0; i < M; i++) {
if (isSelected[i]) {
// 완성된 부분집합에 포함된 요소이면
}
}
// 조합을 완성했을 때 처리할 로직
return;
}
isSelected[cnt] = true; // 선택
subset(cnt + 1);
isSelected[cnt] = false; // 비선택
subset(cnt + 1);
}
작성중