동적계획법
- 문제를 더 작은 문제로 분할 -> 상향식(Bottom - Up) 으로 문제 해결
- 재귀적으로 하향식(Top - Down)으로 푸는 분할정복과 대비
- 동적 계획법으로 문제를 풀기 위해서는 재귀적 정의(점화식)을 상향식으로 구성
이항계수 - 분할정복
위의 성질을 사용하여 코드를 구성
def binomial(n,k):
if (k==0 or n==k):
return 1
else:
return binomial(n-1, k-1) + binomial(n-1, k)
하지만, 한 이항계수 값을 구하는데 있어서, 중복되는 계산이 여러번 발생하는 문제점(비용증가)이 있다.
따라서, 동적계획의 방법을 생각해 보아야 한다.
동적계획 - 이항계수
위의 식을 하향식(재귀) 가 아닌 상향식으로 변환
B : 이항계수 배열
def bin2(n, k):
B = [[0]*(k+1) for _ in range(n+1)] // B[n][k] 인덱스에 접근하기 위하여
for i in range(n+1):
for j in range(min(i,k)+1):
// i < k 인 경우 i 보다 큰 k 값을 계산할 수 없음
// i > k 인 경우 k 보다 큰 값을 구해도 답을 내는데 쓸모가 없음
// 따라서 min(i,k) 값 까지 계산
if (j==0 or j==i):
B[i][j] = 1
else:
B[i][j] = B[i-1][j-1] + B[i-1][j]
return B[n][k]
성능개선
위의 동적계획법을 활용한 코드의 성능을 개선해보자.
이항계수 성질에서
(n, k) = (n, n-k) 이므로, k가 n/2 보다 큰 값을 계산할 때에는 기존의 값을 활용하여 시간복잡도를 개선할 수 있다.
또한 2차원 배열이 아닌 1차원 배열을 사용하는 아이디어로 공간복잡도 또한 개선할 수 있다.
1차원 배열을 사용하는 아이디어는,
한번의 반복문을 도는것이 n이 증가하는 것이고 본래의 배열에 값을 다음 n의 값으로 채워넣는것이다.
코드를 보면서 이해해보자.
def bin3(n, k):
// 시간복잡도 개선
if(k > n//2):
k = n-k
B = [0] * (k+1)
B[0] = 1
for i in range(1, n+1):
// 모든 이항계수를 구하는것이 아니라 k까지 (필요한만큼만) 계산
j = min(i,k)
while (j>0):
// 쉽게 말해서 배열의 오른쪽부터 업데이트 하는것이다.
// 그 이유는 왼쪽부터 오른쪽으로 갈 경우 이미값이 다음 n 값으로 업데이트 되어 재사용할 수가 없기 때문
// 반면 오른쪽부터 왼쪽으로 갈 경우 바뀐값을 참조할 수 없으므로 적절히 수행 가능
B[j] = B[j] + B[j-1]
j -= 1
return B[k]
'휴지통' 카테고리의 다른 글
Window 환경에서 Rust 설치하기 (0) | 2022.02.08 |
---|---|
플로이드 와샬 (Floyd - Warshall) (0) | 2021.06.01 |
백준 11726 - 2 x n 타일링 : dynamic programming (0) | 2021.05.03 |
백준 N과 M (9) ~ (12) / 파이썬 백트래킹 (0) | 2021.04.22 |
백준 N과 M (1) ~ (8) / 파이썬 백트래킹 (0) | 2021.04.22 |