문제
지원이에게 2진 수열을 가르쳐 주기 위해, 지원이 아버지는 그에게 타일들을 선물해주셨다. 그리고 이 각각의 타일들은 0 또는 1이 쓰여 있는 낱장의 타일들이다.
어느 날 짓궂은 동주가 지원이의 공부를 방해하기 위해 0이 쓰여진 낱장의 타일들을 붙여서 한 쌍으로 이루어진 00 타일들을 만들었다. 결국 현재 1 하나만으로 이루어진 타일 또는 0타일을 두 개 붙인 한 쌍의 00타일들만이 남게 되었다.
그러므로 지원이는 타일로 더 이상 크기가 N인 모든 2진 수열을 만들 수 없게 되었다. 예를 들어, N=1일 때 1만 만들 수 있고, N=2일 때는 00, 11을 만들 수 있다. (01, 10은 만들 수 없게 되었다.) 또한 N=4일 때는 0011, 0000, 1001, 1100, 1111 등 총 5개의 2진 수열을 만들 수 있다.
우리의 목표는 N이 주어졌을 때 지원이가 만들 수 있는 모든 가짓수를 세는 것이다. 단 타일들은 무한히 많은 것으로 가정하자.
풀이
N만큼 이어진 타일열 모두를 생각해보자. 이들 모두의 맨 마지막에는 1이 있거나 00이 있는 것은 자명하다.
맨 마지막에 타일 '1'이 있는 경우, 이 마지막 1과 그 앞에 있는 N-1개의 타일들은 서로 독립적으로 존재한다. 즉, 마지막의 1 타일이 나머지들의 배치에 전혀 영향을 주지 않고, 그렇기에 나머지들은 나머지대로 자유롭게 배열할 수 있는 것이다. 마치 초콜릿에서 한 칸을 떼어내도 다른 칸들은 온전히 남아 있는 것과 같다. 따라서 맨 마지막에 타일 1이 있는 배열의 개수는 N-1만큼 이어진 타일열의 개수와 같다.
맨 마지막에 타일 '00'이 있는 경우도 동일한 방법으로, N-2만큼 이어진 타일열의 개수와 같다. 따라서 N만큼 이어진 타일열의 개수를 f(N)이라 하면
f(N) = f(N-1) + f(N-2)가 얻어진다.
거창한 것 같았지만 피보나치 수열이다...
코드
간단한 피보나치 수열이지만, 여기서 문제의 조건이 발목을 잡는다.
시간 | 제한 메모리 |
0.75 초 (추가 시간 없음) | 256 MB |
꽤나 까다롭다. 시간은 DP를 조금 엄격하게 다루면 쉽게 해결할 수 있을 것 같지만, 메모리는 1000000번째 항에까지 접근할 수 있으니 부족하다.
시도했던 방법을 전부 나열해보려 한다.
1. 재귀를 이용하는 방법
# 재귀 제한을 풀어주기 위해 setrecursionlimit를 이용한다.
import sys
sys.setrecursionlimit(1000004)
t = [None]*1000004
def tile(n):
if n == 1:
return 1
if n == 2:
return 2
if t[n] != None:
return t[n]
return tile(n-1)+ tile(n-2)
N = int(input())
print(tile(N)%15746)
결과: 메모리 초과
2. 변수 2개를 이용한 반복문
앞에서 메모리 초과가 떴기에 변수를 두 개로 줄이면 메모리 초과가 안나지 않을까 생각이 들었다.
N = int(input())
#초기설정
a = 1
b = 2
#a, b 번갈아가면서 그다음 피보나치값 대입
for i in range(2, N):
if a>b:
b = a+b
else:
a = a+b
#결과적으로 더 큰 쪽이 f(N)일 것이니 그것을 출력
if a>b:
print(a%15746)
else:
print(b%15746)
결과: 시간 초과
3. for문
for문으로 코드를 작성해도 메모리 초과가 났다. 뭔가 이상해서 약간의 자료 조사를 한 결과, 너무 큰 수들이 저장되고 있어서 메모리 초과가 지속해서 나는 것이었다. 그래서 계산 과정에서 계속 15746으로 나눈 나머지를 대신 저장해야 한다고 한다.
N = int(input())
tile = [None]*(N+1) #N+1로 하는 이유는 N = 1일 경우 tile[1]에 2를 대입하는 아래의 코드에서 에러가 생김
tile[0] = 1
tile[1] = 2
for i in range(2, N):
tile[i] = (tile[i-1]+tile[i-2])%15746 #메모리 초과 방지
print(tile[N-1])
리뷰
2번 방법은 아마 for문 안의 조건문 때문에 시간 초과가 났을 것 같다. 그러면 아마 1번 방법에서도 3번 방법처럼 15746을 지속적으로 나눠준다고 해도 시간 초과가 날 것이다. (재귀문 안에 자그마치 3개의 조건문이 있으니) 다만, 3번 방법은 수많은 재귀로 인해 메모리 초과가 난 것도 한몫을 한 것으로 예상되어 검증은 할 수 없었다.
'PS' 카테고리의 다른 글
백준 2579번_계단 오르기 (Python) (0) | 2022.08.22 |
---|---|
백준 1932번_정수 삼각형 (Python) (0) | 2022.08.15 |
백준 1149번_RGB거리 (Python) (0) | 2022.08.08 |
백준 24416번_알고리즘 수업 - 피보나치 수 1 (Python) (0) | 2022.06.27 |
정보올림피아드 2022 고등부 2교시 1번 (0) | 2022.05.20 |