-
Dynamic Programming(1)알고리즘 2020. 6. 19. 01:13
다이나믹 프로그래밍의 가장 큰 컨셉은
"subproblem의 결과를 저장해서 사용한다"
그래서, 2가지가 가장 중요한 포인트라고 생각하는데,
1. subproblem을 어떻게 만들것인가? (90%)
2. 이걸 어떤 형식으로 저장하고, 사용할 것인가? (10%)
subproblem을 어떻게 만들것인가를 아는 순간 2번은 자연스럽게 나올것이라고 생각된다.
다이나믹 프로그래밍을 다루는(?) 2방식이 있는데,
첫번째는 "Bottom to Top" 으로 일단 inital부터 내가 구하는 값까지의 데이터를 다 저장한 뒤, 꺼내쓰는 방법
두번째는 "Top to Bottom(memoized)"으로 재귀를 통해 값들을 채워나가는 방법
아마 두번째가 더 많이 쓰이는 것 같기는 한데, 처음 다이나믹 프로그래밍을 다룰때 빠른 이해는 1번. 조금 익숙해지면 2번이 더 간결하고 직관적인 느낌적인 느낌느낌.
아무튼 대표적인 Dynamic programming 문제 ROD cutting 과 LCS를 각각 2가지 방식으로 풀어보았다.
문제 : ROD cutting
나무길이와 나무 길이당 가격이 주어질때 나무를 어떻게 잘라야 가장 높은 가격을 받을 수 있는가?
생각해보면 엄청 간단한 subproblem이 보인다.
P에 0부터 충분히 큰수까지의 길이당 price가 있다고 가정
예를들어 ROD(4)를 구한다면,
max( P[1]+ROD(3),P[2]+ROD(2),P[3]+ROD(1),P[4])
P는 바로 구할수 있는거고, 만약 4보다 작은 ROD(n)의 데이터가 있다면, 바로 꺼내쓸수 있다.
def b_to_u_rod(length,prices): r=[0] for i in range(1,length+1): q=0 for j in range(1,i+1): q=max(q,prices[j]+r[i-j]) r.append(q) return r[length]
ROD bottom to top
r : 과거 데이터 저장
for 구문에서 1부터 length까지 하나씩 돌면서, 해당 나무 길이에서의 최대 수익을 리스트 r 에 저장
def t_to_d_rod(length,prices,r): if r[length]>=0:#값을 가지고 있다면 return r[length] elif length==0: q=0 else:#no value q=-10000 for i in range(1,length+1): q=max(q,prices[i]+t_to_d_rod(length-i,prices,r)) r[length]=q print(r) return q
ROD memoized
재귀방식으로 값을 불러오면서, 계산.
데이터를 저장하는 리스트 r을 매개변수로 받으면서 업데이트
문제 : LCS (Longest Common Subsequence)
2개의 문자열, 공통의 subsequence 중 최장 subsequence 길이
(솔직히 이건 좀 어려웠던 기억쓰.. subproblem으로 만드는데도 꽤 걸렸다.
그래서 하나 기억하고 있는건 뒤에서부터 맞추기)하 설명 너무 못하겠다...ㅠ
예를들어 "ABC","BCAC"의 LCS구한다고 했을때,
나는 맨 마지막 원소만 비교를 한다.
만약에 맨마지막 원소가 같다면, 각 문자열의 마지막 원소를 제외한 문자열들의 LCS값+1이 LCS가 될것이다.
LCS("ABC","BCAC")=1+LCS("AB","BCA")
그리고 만약, 맨 마지막 원소가 다르다면, 문자열중 하나씩 마지막 원소를 뺏을때의 최대 LCS값이 LCS가 될것이다.
LCS("AB","BCA")=max(LCS("AB","BC") , LCS("A","BCA"))
이런식으로 가다보면, " "이되는 문자열을 만나는 순간 LCS값이 0이 될거고,
이걸 시작으로 차근차근 저장하면,
빠르게 LCS값을 구할수 있다는.. 방법..!
저 장황한 설명을 그림으로 표현하면 요렇게!
예를들어 표의(6,1)은 LCS("ABCBDA","B")를 구하는것으로 이해하면 된다.
그러면 맨뒤의 원소들이 다르므로 max (LCS("ABCBD","B"),LCS("ABCBDA",""))값이고,
이미 저장되어있는 표에는 LCS("ABCBD","B")=1 / LCS("ABCBDA","")=0이므로, LCS("ABCBDA","B")=1 이다.
장황하다.. 됴르륵
아무튼 Python으로 LCS풀이 구현
def lcs1(X,Y): n=len(X) m=len(Y) T=[[0 for j in range(m+1)] for i in range(n+1)] for i in range(1,n+1): for j in range(1,m+1): if X[i-1]==Y[j-1]: T[i][j]=1+T[i-1][j-1] else: T[i][j]=max(T[i-1][j],T[i][j-1]) return T[n][m]
LCS Bottom to Top
T 과거데이터 저장 2차원 리스트
똑같이 for 문을 돌면서 T에 차곡차곡 쌓는다.
def lcs2(X,Y,T): n=len(X) m=len(Y) if T[n][m]>=0: #값이 있다면 return T[n][m] elif n==0 or m==0: T[n][m]=0 else: if X[n-1]==Y[m-1]: T[n][m]=1+lcs2(X[:(n-1)],Y[:(m-1)],T) else: T[n][m]=max(lcs2(X[:(n-1)],Y[:m],T),lcs2(X[:n],Y[:(m-1)],T)) return T[n][m]
LCS memoized
다이나믹 프로그래밍 관련 문제도 몇개 더 풀어야징🐥
참고 |
'알고리즘' 카테고리의 다른 글
[DFS]1987 (0) 2020.11.05 [DP]1915 (0) 2020.10.31 sort 알고리즘 (0) 2020.06.22 Divide and Conquer(1) (0) 2020.06.17 dijkstra 알고리즘 (0) 2020.06.11