오늘 배운 것들
최장 공통 부분 수열(LCS)를 구하는 문제
2-1. 조건 확인
수열 X=(x0,x1,...,xn),Y=(y0,y1,...,ym)의 LCS(LCSXY)를 구한다고 하자.
부분 수열 Xn−1=(x0,x1,...,xn−1)이라고 하면
- 최적 부분 구조
- xn=ym일 때
LCSXY=LCSXn−1Ym−1+xn
- xn=ym일 때
LCSXY=max(LCSXn−1Ym,LCSXnYm−1)
- 중복 부분 문제
LCSXn−kYm−k(k>1)를 구하는 것은 LCSXn−1Ym−1, LCSXn−1Ym, LCSXnYm−1을 구하는 문제의 부분 문제이다.
2-2. LCS 길이 찾기
반복문을 이용한 상향식(Bottom-up) 접근을 사용한다.
최적 부분 구조에서 살펴본 식을 살짝 변형하면 (c[i,j]=length(LCSXiYj))
- xi=yj일 때
c[i,j]=c[i−1,j−1]+1
- xn=ym일 때
c[i,j]=max(c[i−1,j],c[i,j−1])
이므로 이를 이용하여
수열 X의 개수를 1부터 늘려가며 수열 Y와의 LCS길이를 찾는다.
c[i−1,j],c[i,j−1],c[i−1,j−1]는 이전에 계산한 값을 사용한다.(메모이제이션)
import sys
input = sys.stdin.readline
def main() -> None:
A = input().rstrip()
B = input().rstrip()
N_A = len(A)
N_B = len(B)
arr = [[0]*(N_B+1) for _ in range(N_A+1)]
for i in range(1, N_A+1):
for j in range(1, N_B+1):
if A[i-1] == B[j-1]:
arr[i][j] = arr[i-1][j-1]+1
else:
arr[i][j] = max(arr[i-1][j], arr[i][j-1])
print(arr[N_A][N_B])
if __name__ == "__main__":
main()
2-3. LCS 수열 출력
2-2에서 구한 행렬c로 LCS수열을 역참조할 수 있다.
조건문을 역으로 뒤집으면 된다.
- c[i,j]=c[i−1,j−1]+1일 때
xi=yj
c[i−1,j−1]를 탐색
- c[i,j]=c[i−1,j]일 때
xn=ym
c[i−1,j]를 탐색
- c[i,j]=c[i,j−1]일 때
xn=ym
c[i,j−1]를 탐색
0-1 배낭 문제
물건들의 무게와 가치가 주어지면, 제한된 무게 안에서 최대 가치를 가지는 조합을 찾는 문제
3-1. 조건 확인
물건들 I=((w0,v0),(w1,v1),...,(wn,vn)), 담을 수 있는 최대 무게 K가 주어졌을 때 최대 가치 VI,K를 구한다고 하자.
부분 수열 In−j=((w0,v0),(w1,v1),...,(wn−j,vn−j))(j>n)과 최대 무게 K0(K0<K)가 주어졌을 때의 최대 가치를 VIn−k,K0라고 하면
- 최적 부분 구조
VI,K=max(VIn−1,K,VIn−1,K−wn+vn)
- 중복 부분 문제
위 값은 반복해서 나타난다.
3-2. 코드
반복문을 이용한 상향식 접근을 사용한다.
def main() -> None:
N, K = map(int, input().split())
ITEMS = [None] + [tuple(map(int, input().split())) for _ in range(N)]
arr = [[0 for _ in range(K+1)] for _ in range(N+1)]
for n in range(1, N+1):
for k in range(1, K+1):
w, v = ITEMS[n]
if w <= k:
arr[n][k] = max(arr[n-1][k], arr[n-1][k-w]+v)
else:
arr[n][k] = arr[n-1][k]
print(arr[N][K])
if __name__ == "__main__":
main()