Algorithm/경우의 수
2022. 4. 10. 21:53
- 순열 (Permutation) -
n개의 수를 중복을 허용하지 않고(visited)
r개를 나열할 수 있는 모든 경우의 수 (1 ≤ r ≤ n)
문제인식)
0부터 9(n)까지의 수가 있다.
이들을 이용하여 4(r)개의 수를 중복없이 나열하고자 할 때
가능한 모든 경우를 각각 한 줄씩 출력하고자 한다면 어떻게 해야할까?
int n=9, r=4;
해결과정)
1) 출력에 이용할 배열(a[4]-주황)과 숫자의 중복방지를 위한 방문여부배열(visited[10]-파랑)을 준비한다.
2) 이 배열의 인덱스(idx)마다 0~9까지 삽입하는 반복문을 실행한다. idx는 재귀를 위한 인덱스.
3) 현재 인덱스(idx)에서 사용한 수(i)를 다음 인덱스(idx+1)에서 사용하지 않기 위해 방문처리(visited[i]=1)한다. 그러고나서 다음 인덱스(idx+1)로 재귀진입한다.
4) 인덱스가 나열하고자 하는 수의 갯수(r)를 초과한다면(기저조건) 한 줄씩 출력을 실행한다.
5) 재귀진입이 끝나면 해당 숫자를 다시 사용하기 위해 방문처리를 해제한다.
다음 그림은 재귀가 깊이 한 번 진행되었을 때의 그림을 나타낸 것이다.
주황색 칸은 a배열, 파란색 칸은 visited배열을 나타낸다.
5)번 설명에 대한 설명을 더 하자면,
예를 들어 위 그림에서 "idx+3"에 삽입된 "3"을 방문처리 한 후 다음 재귀는 출력을 진행하고 끝난다.
그렇다면 "idx+2"에서 "3"을 삽입하기 위해선 방문처리했던 "3"을 다시 해제해야 한다.
(※ [더보기]를 클릭하면 C/C++ 소스코드를 참조할 수 있습니다.)
#include <cstdio>
int n=9, r=4;
int a[4];
bool visited[10]; //방문여부배열
void Permutation(int idx) {
if(idx>=r) {
//출력
for(int i=0; i<r; i++) printf("%d ", a[i]); printf("\n");
//종료
return;
}
for(int i=0; i<=n; i++) { //i는 a배열에 삽입하기 위한 수
if(visited[i]) continue; //중복방지
a[idx]=i;
visited[i]=true; //재귀진입전 방문처리
Permutation(idx+1); //재귀진입
visited[i]=false;
}
}
int main() {
Permutation(0);
return 0;
}
해당 소스코드를 컴파일, 실행하면
각 줄마다 순열의 경우가 하나씩 오름차순으로 출력하게 된다.
위 코드의 경우 시간복잡도는 당연히 O(n의 r제곱)이다.
함수사용)
사실 순열을 탐색하는 방법 중에는
next_permutaion()과 prev_permutation() 함수를 이용하는 방법이 있다. (각각 오름차순, 내림차순)
이 둘은 n개의 수를 r개 고르는 것이 아닌 n개를 완전탐색하는 방식이다.
이 둘을 사용하는 방법은 여기를 참조
응용)
내림차순으로 출력해보기
중복을 허용하는 순열 출력해보기(사실 더 간단하다.)
(※ [더보기]를 클릭하면 C/C++ 소스코드를 참조할 수 있습니다.)
#include <cstdio>
int n=9, r=4;
int a[4];
void f(int idx) {
if(idx>=r) {
//출력
for(int i=0; i<r; i++) printf("%d ", a[i]); printf("\n");
//종료
return;
}
for(int i=0; i<=n; i++) { //i는 a배열에 삽입하기 위한 수
a[idx]=i;
f(idx+1); //재귀진입
}
}
int main() {
f(0);
return 0;
}
수를 재귀적으로 삽입하는 방식이 아닌, 재귀적으로 위치를 맞바꾸는 방식으로 해보기
등
'Algorithm > 경우의 수' 카테고리의 다른 글
조합 (Combination) (0) | 2022.04.20 |
---|---|
순열 (Permutation) - 교환방식 (0) | 2022.04.11 |