분류: 조합, 백트래킹
문제
문제 설명
자연수 N과 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오.
- 1부터 N까지 자연수 중에서 중복 없이 M개를 고른 수열
- 고른 수열은 오름차순이어야 한다.
입력
첫째 줄에 자연수 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 8)
출력
한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다.
수열은 사전 순으로 증가하는 순서로 출력해야 한다.
풀이
global N, M, pool
def dfs(lst: list[int], poolStart: int):
global N, M, pool
depth = len(lst)
if depth == M:
print(*lst)
return
for i in range(poolStart, N):
if depth + N - i < M:
continue
lst.append(pool[i])
dfs(lst, i + 1)
lst.pop()
def main():
global N, M, pool
N, M = map(int, input().split())
pool = tuple(range(1, N+1))
dfs([], 0)
main()
모든 조합 구하기
트리의 깊이는 최대 M.
리프(깊이 M)에 도착하면 조합 원소 하나 완성
`poolStart`로 pool의 범위를 줄여 중복을 예방([3, 4]와 [4, 3]이 나오지 않도록)
`depth + N - i`: 풀에 남은 모든 원소의 개수(N - i)와 현재 `lst`의 길이 `depth`를 더한 값. 즉, lst에 pool의 모든 값을 넣어도 M의 길이로 만들 수 있는지 확인(백트리킹)
N=4, M=3, lst=[1,4]인 상황을 만들지 않음.
- itertools
itertools 라이브러리의 조합형 이터레이터 사용
`combinations` 조합
from itertools import combinations
N, M = map(int, input().split())
for c in combinations(range(1, N+1), M):
print(*c)
- combinations 구현
import sys
read = sys.stdin.readline
def combinations(pool: list[int], n: int, r: int):
if r == 0 or r > n:
return []
if r == 1:
return [[x] for x in pool]
result = []
for i, x in enumerate(pool):
nextPool = pool[i+1:]
for c in combinations(nextPool, n - 1, r - 1):
result.append([x] + c)
return result
def main():
N, M = map(int, read().split())
pool = list(range(1, N+1))
result = combinations(pool, N, M)
for c in result:
print(*c)
main()
`nextPool = pool[i+1:]`으로 현재 선택한 요소의 뒷 요소로 다음 풀을 지정
python에서 슬라이싱은 인덱스를 벗어나도 오류가 나지 않는다.
lst = [1, 2, 3]일때 lst[9]는 index out of range 오류이지만 lst[9:]는 빈 리스트릴 반환한다.
다라서 `i`의 최대 값이 `len(pool) - 1`임에도 `pool[ i + 1 :]`을 사용할 수 있다.
'Problem Solving > BOJ' 카테고리의 다른 글
[백준 - 15651] N과 M (3) - Python (0) | 2023.09.09 |
---|---|
[백준] N과 M 시리즈 풀이 모음 (0) | 2023.09.09 |
[백준 - 15649] N과 M (1) - Python, C++ (0) | 2023.09.09 |
[백준 - 9019] DSLR - Python (0) | 2023.09.09 |
[백준 - 7569] 토마토 - C++, Python (0) | 2023.09.08 |