DP(동적계획법)
728x90

메모이제이션(memoization)

- 컴퓨터 프로그램을 실행할 때 이전에 계산한 값을 메모리에 저장해서 매번 다시 계산하지 않도록 하여 전체적인 실행속도를 빠르게 하는 기술

- 추가적인 메모리 공간이 필요하다

- 재귀 함수 호출로 인한 시스템 호출 스택을 사용하게 되고 실행 속도 저하 또는 오버플로우가 발생할 수 있다

 

재귀

if(n<N) return n;
return DP(n-1)+DP(n-2);

 

메모이제이션을 사용한 재귀

if(n>=N && memo[n]==0) memo[n] = DP(n-1)+DP(n-2);
return memo[n];

 

동적계획법(Dynamic Programming)

그리디 알고리즘과 같이 최적화 문제를 해결하는 알고리즘

먼저 작은 부분 문제들의 해들을 구하고 이들을 이용하여 보다 큰 크기의 부분 문제들을 해결하여, 최종적으로는 원래 주어진 문제를 해결하는 알고리즘 설계 기법

재귀(하향식) n항을 구하기 위한 n-1과 n-2... 항들을 구한다

DP(상향식) n항을 구하기 1, 2, ... n-2, n-1 항들을 구한다

 

- 다음과 같은 요건을 가지고 있어야 한다

1) 최적 부분문제 구조(Optimal substructure)

2) 중복 부분문제 구조(Overlapping subproblems)

 

최적 부분문제 구조(Optimal substructure)

- 최적화의 원칙(Principle of Optimality) : 어떤 문제에 대한 해가 최적일 때 그 해를 구성하는 작은 문제들의 해 역시 최적이어야 한다

- 만약 큰 문제의 최적해가 작은 문제들의 최적해들로 구성되지 않는다면 이 문제는 동적 계획법을 사용할 수 없다

- 최적의 원칙이 적용되지 않는 예: 최장경로(Longest Path) 문제

 

중복 부분문제 구조(Overlapping subproblems)

- DP는 큰 문제를 이루는 작은 문제들을 먼저 해결하고 작은 문제들의 최적 해(Optical Solution)를 이용하여 순환적으로 큰 문제를 해결한다

>> 순환적인 관계(recurrence relation)를 명시적으로 표현하기 위해서 동적 계획법에서는 일반적으로 수학적 도구인 점화식(어떤 수열의 각각의 항들의 관계를 나타낸 식)을 사용한다

- DP는 문제의 순환적인 성질 때문에 이전에 계산되었던 작은 문제의 해가 다른 어딘가에서 필요하게 되는데(Overlapping subproblems) 이를 위해 DP에서는 이미 해결된 작은 문제들의 해들을 어떤 저장 공간(table)에 저장하게 된다

- 저장된 해들이 다시 필요할 때마다 해를 얻기 위해 다시 문제를 재계산하지 않고 table의 참조를 통해서 중복된 계산을 피하게 된다

 

분할 정복과의 차이

분할 정복 DP
- 연관 없는 부분 문제로 분할
- 부분문제를 재귀적으로 해결
- 부분문제의 해를 결합(combine)한다
- 예: 병합 정렬(결합), 퀵 정렬(결합 x)
- 하향식 방법으로 접근
- 부분 문제들이 연관이 없으면 적용할 수 없다
- 부분 문제들 사이에 의존적 관계 존재
- 관계가 문제에 따라 다르고, 대부분의 경우 뚜렷하게 보이지 않아서 함축적인 순서(implicit order)라고 한다
- 모든 부분 문제를 한 번만 계산하고 결과를 저장하고 재사용한다
- 상향식 방법으로 접근

 

DP 적용 접근 방법

1) 최적해 구조의 특성을 파악하라

 - 문제를 부분 문제로 나눈다

2) 최적해의 값을 재귀적으로 정의하라

 - 부분 문제의 최적해 값에 기반하여 문제의 최적해 값을 정의한다

 - 점화식으로 정의

3) 상향식 방법으로 최적해의 값을 계산하라

 - 가장 작은 부분 문제부터 해를 구한 뒤 테이블에 저장한다

 - 테이블에 저장되어 있는 부분 문제의 해를 이용하여 점차적으로 상위 부분 문제의 최적해를 구한다(상향식 방법)

 

DP 적용 알고리즘

for(int i = 0; i<N; i++) {
     DP[i] = DP[i-1] + DP[i-2];
}

 

최단 경로(shorest Path)

- 한 도시에서 다른 도시로 가장 빨리 갈 수 있는 경로를 찾는 문제

- 가중치 포함, 방향성 그래프에서 최단 경로 찾기

>> 가중치가 없는 그래프의 최단(bfs)

>> 양수만 있을 때(다익스트라)

>> 음수의 가중치가 있거나, 모든 쌍의 최단을 구할 때(플로이드-워샬)

- 최적화 문제(optimization problem)

>> 주어진 문제에 대해서 하나 이상의 많은 해답이 존재할 때, 이 가운데에서 가장 최적인 해답(optimal solution)을 찾아야 하는 문제

 

DP 접근 방법

- 각 정점을 시작 정점으로 정하여 다익스트라(Dijkstra)의 최단 경로 알고리즘을 수행

- 정점 1에서 정점 k까지의 모든 정점들을 반드시 경유하는 경로를 의미하는 것이 아니다

1) 부분 문제를 찾는다 -> 정점의 개수를 줄여 본다

2) 부분 문제 정의: 정점만을 경유 가능한 정점들로 고려하여, 정점 i로부터 정점 j까지의 모든 경로 중에서 가장 짧은 경로의 거리

3) 경유 가능한 정점들을 하나씩 늘린다

// 경찰과 도둑으로 암기
for(int k = 1; k<=N; k++) { // 경유지
	for(int i = 1; i<=N; i++) { // 출발지
		for(int j = 1; j<=N; j++) { // 도착지
        		// 경유지를 거쳐서 오는 비용과, 직전 최적해 중 가장 작은 해를 유지
        		DP[i][j] = Math.min(DP[i][k] + DP[k][j], DP[i][j]);
		}
	}
}

 

728x90

'프로그래밍 > ALGORITHM' 카테고리의 다른 글

순열, 조합, 부분집합  (0) 2022.04.07
이분탐색 기본 틀  (0) 2022.03.25