문제
자연수 N과 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오.
- 1부터 N까지 자연수 중에서 중복 없이 M개를 고른 수열
입력
첫째 줄에 자연수 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 8)
출력
한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다.
수열은 사전 순으로 증가하는 순서로 출력해야 한다.
문제 해결
전체 코드
import sys
N,M=list(map(int,sys.stdin.readline().split()))
def permutation(N,M,sequence,visited):
if len(sequence)==M:
print(*sequence)
for i in range(N):
if visited[i]==False:
sequence.append(i+1)
visited[i]=True
permutation(N,M,sequence,visited)
sequence.pop()
visited[i]=False
visited=[False]*(N)
sequence=[]
permutation(N,M,sequence,visited)
이 코드는 N과 M을 입력받고, 1부터 N까지의 숫자 중 M개를 선택하여 만들 수 있는 모든 순열을 출력하는 백트래킹(Backtracking) 알고리즘이다.
코드 해설
import sys
N, M = list(map(int, sys.stdin.readline().split()))
- sys.stdin.readline()을 사용하여 한 줄의 입력을 받는다.
- 입력값을 공백 기준으로 나눈 뒤, int형으로 변환하여 N과 M에 저장한다.
- N: 사용할 숫자의 개수 (1부터 N까지)
- M: 순열에서 선택할 개수
-순열을 만드는 재귀 함수 (permutation)
def permutation(N, M, sequence, visited):
- N: 사용할 숫자의 개수 (1부터 N까지)
- M: 뽑아야 할 숫자의 개수
- sequence: 현재까지 선택한 숫자 리스트
- visited: 해당 숫자를 사용했는지 여부를 나타내는 리스트
기저 사례 (종료 조건)
if len(sequence) == M:
print(*sequence)
return
- sequence의 길이가 M과 같아지면, 즉 M개의 숫자를 모두 선택하면 출력하고 종료.
순열 생성 (백트래킹)
for i in range(N):
if visited[i] == False:
sequence.append(i+1)
visited[i] = True
permutation(N, M, sequence, visited)
sequence.pop()
visited[i] = False
- for i in range(N): 1부터 N까지의 숫자를 차례대로 확인
- if visited[i] == False: 아직 사용하지 않은 숫자라면,
- sequence.append(i+1): 숫자를 추가
- visited[i] = True: 해당 숫자를 사용했다고 표시
- 재귀 호출: permutation(N, M, sequence, visited)을 호출하여 다음 숫자를 선택
- sequence.pop(): 재귀 호출이 끝난 후 숫자를 제거 (백트래킹)
- visited[i] = False: 방문 표시를 원래대로 복구
실행 흐름 (예제)
예제 입력
3 2
과정
1부터 3까지의 숫자 중 2개를 선택한 모든 순열을 구해야 한다.
- 초기 상태: N = 3, M = 2, sequence = [], visited = [False, False, False]
1️⃣ 첫 번째 숫자 선택 (1 선택)
- sequence = [1]
- visited = [True, False, False]
2️⃣ 두 번째 숫자 선택 (2 선택)
- sequence = [1, 2]
- visited = [True, True, False]
- 길이가 M(=2)이므로 출력 → "1 2"
🔙 백트래킹: 2 제거 → sequence = [1], visited = [True, False, False]
2️⃣ 두 번째 숫자 선택 (3 선택)
- sequence = [1, 3]
- visited = [True, False, True]
- 길이가 M(=2)이므로 출력 → "1 3"
🔙 백트래킹: 3 제거 → sequence = [1], visited = [True, False, False]
🔙 백트래킹: 1 제거 → sequence = [], visited = [False, False, False]
1️⃣ 첫 번째 숫자 선택 (2 선택)
- sequence = [2]
- visited = [False, True, False]
2️⃣ 두 번째 숫자 선택 (1 선택)
- sequence = [2, 1]
- visited = [True, True, False]
- 길이가 M(=2)이므로 출력 → "2 1"
🔙 백트래킹: 1 제거 → sequence = [2], visited = [False, True, False]
2️⃣ 두 번째 숫자 선택 (3 선택)
- sequence = [2, 3]
- visited = [False, True, True]
- 길이가 M(=2)이므로 출력 → "2 3"
🔙 백트래킹: 3 제거 → sequence = [2], visited = [False, True, False]
🔙 백트래킹: 2 제거 → sequence = [], visited = [False, False, False]
1️⃣ 첫 번째 숫자 선택 (3 선택)
- sequence = [3]
- visited = [False, False, True]
2️⃣ 두 번째 숫자 선택 (1 선택)
- sequence = [3, 1]
- visited = [True, False, True]
- 길이가 M(=2)이므로 출력 → "3 1"
🔙 백트래킹: 1 제거 → sequence = [3], visited = [False, False, True]
2️⃣ 두 번째 숫자 선택 (2 선택)
- sequence = [3, 2]
- visited = [False, True, True]
- 길이가 M(=2)이므로 출력 → "3 2"
🔙 백트래킹: 2 제거 → sequence = [3], visited = [False, False, True]
🔙 백트래킹: 3 제거 → sequence = [], visited = [False, False, False]
🔹 최종 출력 결과
1 2
1 3
2 1
2 3
3 1
3 2
시간 복잡도
- N! / (N-M)! 개의 순열을 만들므로, 최악의 경우 O(N!)이다.
- N이 커질수록 시간이 급격히 증가하므로, 효율적으로 백트래킹을 적용해야 한다.
라이브러리를 이용해 구현해 보자! (feat. itertools.permutation())
직접 구현한 permutation을 python에서 제공하는 itertools.permutation()으로 똑같이 구현할 수 있다.
itertools.permutations()은 Python의 C로 구현된 내부 라이브러리 함수이므로, 우리가 Python에서 직접 재귀 호출을 사용하는 것보다 훨씬 빠르게 동작한다.
- itertools.permutations()은 내부적으로 C 언어 최적화가 적용되어 있어 Python 인터프리터를 통한 함수 호출과 메모리 관리 비용을 줄인다.
- 반면, 직접 구현한 백트래킹 방식은 Python의 재귀 호출 비용과 리스트 연산 비용이 포함되어 상대적으로 느리다.
결론적으로 itertools.permutations()이 더 빠르게 동작하는 가장 큰 이유는 C로 구현되어 있어서 함수 호출 오버헤드가 적기 때문이다.
import sys
import itertools
# N과 M 입력 받기
N, M = map(int, sys.stdin.readline().split())
# itertools.permutations을 이용하여 모든 순열 생성 후 출력
for perm in itertools.permutations(range(1, N+1), M):
print(*perm)
'백준 문제 풀이' 카테고리의 다른 글
| [백준 / 파이썬] 백준 1748번 문제 풀이: 수 이어 쓰기 1 (0) | 2025.01.16 |
|---|---|
| [백준 / 파이썬] 백준 6064번 문제 풀이: 카잉 달력 (0) | 2025.01.15 |
| [백준 / 파이썬] 백준 1107번 문제 풀이: 브루트 포스 (Brute Force) (0) | 2024.09.16 |
| [백준 / 파이썬] 백준 1476번 문제 풀이: 브루트 포스 (Brute Force) (1) | 2024.09.15 |
| [백준 / 파이썬] 백준 3085번 문제 풀이: 브루트 포스 (Brute Force) (0) | 2024.09.14 |