오늘 배운 부분집합을 복습하려고 고른 문제였는데, 시간 제한이 1초라서 배운 방법을 적용하기가 쉽지 않았다.😥
다음과 같이 모든 음이 아닌 3의 제곱수가 들어있는 집합이 있다.
S = {1, 3, 9, 27, 81, ...}
S의 모든 부분집합을 구한 뒤, 이 집합을 가지고 있는 원소값의 합으로 오름차순 정렬했을 때, n번째 집합을 구하는 프로그램을 작성하시오.
입력
입력을 여러 개의 줄로 이루어져 있고, 각 줄에는 n이 주어진다. n은 19자리를 넘지 않는 양의 정수이다. 입력의 마지막 줄에는 0이 있다.
1 7 14 783 1125900981634049 0
출력
각 입력에 대해서, n번째 집합을 예제 출력 형식과 같게 출력한다.
{ } { 3, 9 } { 1, 9, 27 } { 3, 9, 27, 6561, 19683 } { 59049, 3486784401, 205891132094649, 717897987691852588770249 }
input으로 n을 받아서, S의 n번째 부분집합을 출력하는 문제이다.
수 시간 고민하다가 답지 찬스를 쓰고 말았다. 답지를 보고 공부해서 다시 적용시켜 해결했다.
while True: n = int(input()) if n == 0: break n -= 1 ---------------------------------------(a) print("{", end=" ") i = 0 while n != 0: if n % 2: print(3**i, end=", " if n // 2 else " ") ---(b) n //= 2 i += 1 print("}")
모범 답안에서 핵심은 (a)와 (b) 두 부분이었다.
이 문제와 답안을 이해하기 위해서는 먼저 부분집합과 2진법 숫자간의 관계를 알아야 한다.S = [1, 3, 9]의 부분집합을 예시로 들어 설명해보겠다.
S의 부분집합은 [ ], [1], [3], [1, 3], [9], [1, 9], [3, 9], [1, 3, 9] 순으로 총 8개이다.
원소가 3개이므로 2^3개의 부분집합이 있는 것이다.개수가 2^n인 점을 염두에 두고 다음 표를 살펴보자.
순서 집합 2진수 구상 0 [ ] 000 [X X X] 1 [1] 001 [X X 1] 2 [3] 010 [X 3 X] 3 [1, 3] 011 [X 3 1] 4 [9] 100 [9 X X] 5 [1, 9] 101 [9 X 1] 6 [3, 9] 110 [9 3 X] 7 [1, 3, 9] 111 [9 3 1] 2진수에서 1과 0을 각각 on/off 스위치로 생각해보자.
S = [1, 3, 9]인 집합에서 숫자처럼 [9, 3, 1]로 배열한 후 2진수에 대입하여 on/off를 실시하면 S의 n번째 부분집합을 구할 수 있다.※ 문제 조건의 오름차순 정렬은 S가 3의 n승인 집합이므로 신경쓰지 않아도 된다.
앞의 원소를 아무리 더해도 뒤의 원소보다 작기 때문이다.따라서 답안을 다시 살펴보면
(a)는 n이 1일 때 공집합이 되어야 하므로 위 표에서 '순서'값에 맞도록 n에 -1을 해준 것이고,
(b)는 n을 2진수화하여 표의 '구상'대로 출력하기 위한 코드이다.
while True: n = int(input()) if n == 0: break
0이 입력될 때까지 반복하도록 while True로 시작한다.
n += -1 arr = [] a = 0 while True: arr.append(3 ** a) a += 1 if n < 2 ** a: # 입력값 n이 부분집합의 갯수 2**a보다 작을 때까지만 루프 break P = [] for i in range(len(arr)): if n & (1<<i): P.append(arr[i])
0에서 알아본 것처럼 n이 1일 때 '순서'가 0인 공집합을 출력하기 위해서, n값을 -1해준다.
그리고 문제 조건에서 소요 시간이 1초로 짧았기 때문에
집합 S 대신 입력값에 따라 3의 a승을 포함하는 집합 arr를 설정했다.
P 는 설정된 arr 집합의 n번째 부분집합이 된다.
print('{ ', end = '') for i in P: if i == ' ': print('}') break elif i == P[-1]: print(f'{i}', end = ' ') else: print(f'{i}', end = ', ') print('}')
이제 남은건 P의 값을 형식에 맞게 출력해주는 것이다.
출력 형식이 대괄호와 띄어쓰기를 포함하고 있어서 상당히 애를 먹었다.
# 입력값 받기(0이 나올 때까지 루프)
while True:
n = int(input())
if n == 0:
break
# 문제 조건을 맞추기 위하여 n - 1
n += -1
# 3의 a승 집합인 arr 설정. 입력값 n에 따라 arr의 길이가 달라진다.
arr = []
a = 0
while True:
arr.append(3 ** a)
a += 1
if n < 2 ** a: # 입력값 n이 부분집합의 갯수 2**a보다 작을 때까지만 루프
break
# arr의 n번째 부분집합 리스트 P 설정
P = []
for i in range(len(arr)):
if n & (1<<i):
P.append(arr[i])
# P의 값을 형식에 맞게 출력
print('{ ', end = '')
for i in P:
if i == ' ':
print('}')
break
elif i == P[-1]:
print(f'{i}', end = ' ')
else:
print(f'{i}', end = ', ')
print('}')
*모든 문제의 저작권은 백준 온라인 저지(https://www.acmicpc.net/) 원작자에게 있습니다.
백준 4312번(https://www.acmicpc.net/problem/4312)
모범답안 출처(https://home-body.tistory.com/177)