알고리즘 동적프로그래밍
동적 프로그래밍 (dynamic programming)
하위의 작은 문제들을 풀고, 이를 이용해 더 큰 문제를 풀어나가는 방식.
격자(grid)
로부터 시작한다.
-
어떤 제한 조건이 주어졌을 때 무언가를 최적화하는 경우에 유용하다.
-
하위 문제가 서로 의존하지 않는 경우에만 사용할 수 있다.
-
모든 동적 프로그래밍의 답안에는 격자가 있다.
-
격자의 각 칸에는 최적화하고자 하는 값을 적는다. (배낭문제의 경우 모든 물건의 총 가치)
-
각 칸은 원래 문제에 대한 하위 문제이고, 다른 문제를 하위 문제로 가질 수 있다. 그러므로 원래의 문제를 어떻게 하위 문제로 나눌 수 있을지 생각해야 한다. 그렇게 하면 격자의 각각의 축이 어떻게 되어야 하는지 알아내는데 도움이 된다.
-
해답을 계산해 주는 쉬운 공식이 존재하지 않는다.
최장 공통 부분 문자열
격자 만들기
- 각 칸에 넣을 숫자는 무엇인가
- 문제를 어떻게 하위 문제로 나눌 수 있는가
- 격자의 축은 무엇인가
동적 프로그래밍에서는 무언가를 최대화 하는 것이 목표이다.
두 단어에서 공통적으로 가지는 가장 긴 부분 문자열, 즉 최장 공통 부분 문자열(LCS : longest common substring)을 찾는 경우
//hish와 fish의 최장 공통 부분 문자열을 찾는 경우 격자.
//단어를 직접 비교하는 대신에 his와 fis라는 단어를 먼저 비교한다.
//각 칸에 이 두 단어의 최장 공통 부분 문자열의 길이를 쓴다.
//그러면 각각의 축이 그 두 단어를 의미함을 알 수 있다.
//격자는 아래와 같아진다.
H I S H
┌────┐┌────┐┌────┐┌────┐
F │ 0 ││ 0 ││ 0 ││ 0 │
└────┘└────┘└────┘└────┘
┌────┐┌────┐┌────┐┌────┐
i │ 0 ││ 1 ││ 0 ││ 0 │
└────┘└────┘└────┘└────┘
┌────┐┌────┐┌────┐┌────┐
s │ 0 ││ 0 ││ 2 ││ 0 │
└────┘└────┘└────┘└────┘
┌────┐┌────┐┌────┐┌────┐
h │ 1 ││ 0 ││ 0 ││ 3 │
└────┘└────┘└────┘└────┘
//각 칸을 채우기 위한 공식은 아래와 같다.
1. 만약 글자가 다르면 값은 '0' 이다.
2. 만약 글자가 같으면 값은 '좌측 상단 칸의 값+1'이 된다.
구현
if word_a[i] == word_b[j]: # 글자가 같은 경우
cell[i][j] = cell[i-1][j-1]+1
else : # 글자가 다른 경우
cell[i][j] = 0
최장 공통 부분열(longest common subsequence)
두 단어에서 순서가 바뀌지 않고 공통으로 들어간 개수를 최대화 하는 경우
격자 만들기
//fosh와 fish의 최장 공통 부분열을 찾는 경우
F O S H
┌────┐┌────┐┌────┐┌────┐
F │ 1 ││ 1 ││ 1 ││ 1 │
└────┘└────┘└────┘└────┘
┌────┐┌────┐┌────┐┌────┐
I │ 1 ││ 1 ││ 1 ││ 1 │
└────┘└────┘└────┘└────┘
┌────┐┌────┐┌────┐┌────┐
S │ 1 ││ 1 ││ 2 ││ 2 │
└────┘└────┘└────┘└────┘
┌────┐┌────┐┌────┐┌────┐
H │ 1 ││ 1 ││ 2 ││ 3 │
└────┘└────┘└────┘└────┘
최장 공통 부분열 = 3
위쪽 칸과 왼쪽 칸 중에서
1. 만약 글자가 같지 않으면 둘 중 더 큰 값을 선택 (최장 공통 부분 문자열 문제와 다른점)
2. 만약 글자가 같으면 이 값은 '좌측 상단 칸의 값+1'이다
구현
if word_a[i] == word_b[j]: # 글자가 같은 경우
cell[i][j] = cell[i-1][j-1]+1
else : # 글자가 다른 경우
cell[i][j] = max(cell[i-1][j], cell[i][j-1])
reference
도서 : Hello Coding 그림으로 개념을 이해하는 알고리즘