정의
모든 가능한 경우의 수 중에서 특정한 조건을 만족하는 경우만 살펴보는 것
DFS(깊이 우선 탐색) + 특정 조건만 탐색
특징
1. 조건들을 하나씩 대입해가면서 만약 조건에 맞지 않는다면 그 즉시 중단하고 다음으로 넘어간다.
2. 상태공간트리를 DFS(깊이우선탐색)으로 탐색한다.
3. 방문 중인 노드에서 더 하위 노드로 가면 해답이 없을 경우, 해당 노드의 하위 트리를 방문하지 않고 부모 노드로 되돌아간다.
가치치기(pruning)
- 유망하지 않으면(하위 노드가 답이 없을 경우) 하위 트리를 방문하지 않는 것.
문제
자연수 N과 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오.
- 1부터 N까지 자연수 중에서 중복 없이 M개를 고른 수열
정답풀이
#백트레킹
#N과 M(1)
N,M = map(int,input().split())
num_list = [i+1 for i in range(N)]
check_list = [False] * N
arr = []
def dfs(count):
#print(arr)
if count == M:
#원소만 빼서 출력하는것 *
print(*arr)
return
for i in range(N):
#print("for i : -------",i)
if check_list[i]:
#print("중복이라 continue",check_list)
continue
#i번쨰는 거쳐갈것이기에 True
check_list[i] = True
arr.append(num_list[i])
#print("append 이후",arr)
#print(check_list)
#현재의 i를 기준으로 가지치기
dfs(count + 1)
# print("재귀함수끝", i,count)
arr.pop()
#print("pop이후",arr)
check_list[i] = False
dfs(0)
Q)check_list는 왜 [False] * N 인가?
->dfs 탐색을 하면서 중복되는 것을 체크하기 위해 기본 설정값을 False로 하는 것이다.
Q)for문은 왜 저렇게 설계되어있는가?
->N = 3 , M = 2인 경우를 생각해보자. 백트레킹 알고리즘은 어떻게 동작할까
결론만 말하자면 중복부분(빨강)에서는 stop하고 중복되지 않은 부분에서는 계속 진행할 것이다. 이제 코드를 하나하나 살펴보자.
dfs(0)에서 for i in range(N) 에서 i = 0 일 때
check_list[0] = True , arr.append(num_list[0])으로 인해
재귀함수 dfs(1)은 기본 값이
check_list = [True,False,False] , arr =[1]인 상태로 시작하게 된다.(dfs(1)부터 코드가 실행중이므로 아직 dfs(0)에서의 arr.pop(), check_List[i] = False 실행 전)
이제 dfs(1)에서 for i in range(N)이 돌면서 i = 0일 때를 생각해보자
check_list[0] == True 이므로 조건문이 성립되어 continue 된다. ( 중복이므로 제외한다. 사진의 빨간 부분)
for i in range(N)이 돌면서 i = 1일 때를 생각해보자
check_list[1] == False 이므로 조건문이 성립되지 않고, 아래 코드가 실행된다.
중복 체크를 하기 위해 check_list[1] == True가 되고 arr = [1,2]가 된다. 이 값을 default로
dfs(2)가 실행되게 되는데 이때에는 if(count == M)이므로 1 2를 출력하게 된다.
dfs(2)에서의 재귀함수가 하나 끝났으므로 밑의 arr.pop() check_list[1] = False코드가 동작하여
dfs(1) ▼
arr [1, 2] -> arr [1]
check_list [True,True,False] -> check-_list [True,False,False]로 바뀐 상태로 i =2 일때를 체크하게 된다.
dfs(1)에서 i =2 일때 마찬가지로 1 3 을 출력하게 된다.
dfs(1) 일때 for 문의 i = 0 에서의 재귀 호출은 모두 끝이 났으니 , 이제 아래 코드인 arr.pop(), check_list[i] = False가 실행되어
arr [1] -> []
check_list [True,False,False] -> check_list [False,False,False]가 된상태로
dfs(0)일 때의 i =1 for문 코드를 읽게 된다.
이런 식으로 진행하면
1 2
1 3
2 1
2 3
3 1
3 2
라는 값이 도출된다.
반드시 알아야할 점
*재귀함수는 실행하면 밑의 코드를 읽지 않고 재귀함수가 끝날 때까지 실행된다.
즉 그 깊이를 끝까지 탐색하고 나서야 종료되므로, 이 부분에서 헷갈리지 말자.
'CS > 알고리즘' 카테고리의 다른 글
백준 9663번 N-Queen 파이썬 풀이 (0) | 2022.03.11 |
---|---|
카카오 2022 코딩테스트 '신고결과받기' 파이썬 풀이 (0) | 2022.03.10 |
백준 11651번 좌표 정렬하기 2 파이썬 풀이 (0) | 2022.03.03 |
백준 2108 통계학 파이썬 풀이 (0) | 2022.03.01 |
백준 10989번 수 정렬 3 파이썬 풀이(메모리 관리) (0) | 2022.02.28 |