Baekjoon Online Judge/동적 계획법(DP)

동적 계획법(DP) 1 (11053 가장 긴 증가하는 부분 수열 LIS)

jh280722 2020. 4. 28. 15:06

DP란 다이나믹 프로그래밍의 약어이다. 한국어로 동적 계획법이라고 말한다.

동적 계획법은 어떠한 복잡하거나 큰 문제를 간단하고 작은 문제로 나누어서 푸는 알고리즘 설계 기법이다.

복잡한 문제가 같은 부분이 반복되는 부분 구조와 최적의 부분 구조를 가지고 있을 때 여러 개의 부분 문제로 나누어 설계하여 풀 수 있다.

그래서 일반적으로 부분 구조를 통해 점화식을 만들고 Bottom-Up 방식과 Top-Down(재귀)  방식을 통하여 전체 문제를 해결한다.

이 동적 계획법의 핵심 기법은 메모리제이션이다. 메모리제이션은 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술이다. 

그래서 DP 배열을 만들어서 그 값들을 미리 저장해놓은 다음 다시 동일한 계산이 필요한 부분 문제를 호출하였을 때 저장된 값을 바로 불러와 사용할 수 있도록 한다.

 

백준의 DP 문제를 통해 설명해보겠다.

1. 11503 가장 긴 증가하는 부분 수열

 

11053번: 가장 긴 증가하는 부분 수열

수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이고, 길이는 4이다.

www.acmicpc.net

이 문제는 알고리즘에서 대표적인 문제인 LIS(Longest Increasing Subsequence) 문제이다.

수열이 주어졌을 때 가장 긴 증가하는 부분 수열을 구하는 문제이다.

이 문제에서 원하는 답은 가장 긴 증가하는 부분 수열의 길이를 구하는 것이다.

그렇다면 우리가 원하는 해는 각 부분 수열의 길이의 최대값이다.

최댓값을 구하기 위해서는 무조건 1번째 수열의 값부터 다 포함하는 게 이득이다. 그리고 구해놓은 값을 다시 구하는 경우를 막기 위해 메모리제이션을 사용하여 길이를 저장해놓을 배열을 만든다.

그럼 이제 이 배열의 인덱스 별 값은 i번째를 마지막으로 하는 가장 긴 증가하는 부분 수열의 길이라고 정의하자.

D[i] = i번째 원소를 마지막으로 하는 LIS의 길이

그렇게 되면 i < j이고 A[i] < A[j] 일 경우 D[j]=max(D[i] + 1) (1 <= i < j )가 된다.

따라서 반복되는 부분 구조와 점화식의 결과가 최적의 구조를 가지므로 DP를 이용하여 문제를 풀 수 있다.

 

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i=0; i<n; i++) {
        cin >> a[i];
    }
    vector<int> d(n);
    for (int i=0; i<n; i++) {
        d[i] = 1;
        for (int j=0; j<i; j++) {
            if (a[j] < a[i] && d[j]+1 > d[i]) {
                d[i] = d[j]+1;
            }
        }
    }
    
    cout << *max_element(d.begin(),d.end()) << '\n';
    return 0;
}