본문 바로가기

코딩테스트 준비/PS

C++ 백준 14003 (가장 긴 증가하는 부분수열5)

백준 14003 (가장 긴 증가하는 부분수열5)

https://www.acmicpc.net/problem/14003

 

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

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (-1,000,000,000 ≤ Ai ≤ 1,000,000,000)

www.acmicpc.net


LIS의 길이와 수열을 O(N*logN)안에 해결해야 하는 문제이다.

 

LIS에 대한 기본이해와 O(N^2)의 시간으로 길이와 수열을 찾는 법, 그리고 O(N*logN)의 시간으로 LIS의 길이만을 찾는 방법은 아래에 있으니 반드시 모두 이해하고 이 글을 읽도록 한다.

 

1. LIS의 길이를 O(N^2)으로 구하는 방법 : 여기(링크)

2. LIS의 길이와 수열을 O(N^2)으로 구하는 방법 : 여기(링크)

3. LIS의 길이를 O(N*log(N))으로 구하는 방법 : 여기(링크)

 

설명


!다시 한번 말하지만 반드시 위 세개 링크에 있는 설명을 모두 이해하고 와야 이 글을 이해할 수 있다!

 

LIS 수열찾는 것을 O(N^2)에 해결하는 방법을 이해했다는 가정하에 설명한다.

위의 방법에서 dp의 값을 채워넣는 것은 O(N^2)이 걸렸지만, 역방향으로 탐색해 조건을 맞춰가며 LIS의 수열을 구하는 것은 O(N)의 시간이 걸렸다. (여기서 dp[index]는 num[index]를 포함했을 때의 LIS 길이이다.)

이게 무슨말인가 하면, O(N*logN)의 시간으로 LIS의 길이를 구하는 과정에서 dp의 값만 저장해놓을 수 있다면 위 방법에서 썼던 수열을 구하는 방식을 그대로 가져다 쓰면 된다는 소리다.(O(N)이기 때문에)

 

LIS의 길이를 O(N*logN)의 시간복잡도로 푸는 방법을 이해했다면 각 단계에서 index별 dp의 값을 저장하는 것은 매우 쉽다.

dp[index] = vector L에 추가되었을 때 그때의 index이다.

다음 예시를 따라가보며 이해해보자.

 

{8,14, 9, 17, 2, 15, 16}라는 수열이 있다.

이 방식을 O(N*logN)의 시간복잡도로 LIS를 구할때의 방법을 쓴다면 과정은 다음과같다.

 

1. {8,14, 9, 17, 2, 15, 16}

num[index] 8 14 9 17 2 15 16
vector L 8            

8은 L에서 index가 1인 곳에 위치가 되었으니 dp[1] = 1이다.

 

2. {8,14, 9, 17, 2, 15, 16}

num[index] 8 14 9 17 2 15 16
vector L 8 14          

14는 L에서 index가 2인 곳에 위치가 되었으니 dp[2] = 2이다.

 

3. {8,14, 9, 17, 2, 15, 16}

num[index] 8 14 9 17 2 15 16
vector L 8 9          

9는 L에서 index가 2인 곳에 위치가 되었으니 dp[3] = 2이다.

 

여기까지만 해도 이해가 될 것이다.

이전 글에서도 언급을 했었지만 기존 LIS에서 배열을 돌며 최적의 위치를 찾는다는 것은 자신보다 작으면서 DP의 값은 가장 큰 index를 찾는 것이었다. (이유는 알고있어야 한다) L의 역할이 바로 이것이다.

만약 이 말이 이해가 되지 않는다면 여기(링크)의 설명을 다시 읽고 오자.

 

위 과정을 모두 거치고 나면 dp배열은 다음과 같다.

num[index] 8 14 9 17 2 15 16
dp[index] 1 2 2 3 1 3 4

자 이제 dp배열을 알았으니 O(N)의 시간으로 LIS수열을 가지고 오면 된다. 이 부분은 여기(링크)에서 설명 했었으니 넘어가겠다.

 

구현


#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;

int main(){
    ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
    int N;
    cin>>N;
    int *num = new int[N];
    int *dp = new int[N];
    int a;
    int idx, dptmp=0;
    vector <int> L;
    stack <int> s;
    vector <int>::iterator p;
    for(int i=0;i<N;i++){
        cin>>a;
        num[i]=a;
        if(L.size()==0) {
            L.push_back(num[i]);
            dp[i]=1;
        }
        else{
            if(L[L.size()-1]<num[i]) {
                L.push_back(num[i]);
                dp[i] = L.size();
            }
            else{
                p = lower_bound(L.begin(),L.end(),num[i]);
                *(p)=num[i];
                dp[i]=p-L.begin()+1;
            }
        }
        if(dp[i]>dptmp) {
            idx = i;
            dptmp = dp[i];
        }
    }
    cout<<L.size();
    s.push(num[idx]);
    for(int i=idx-1;i>=0;i--){
        if(num[i]<num[idx] && dp[i]+1 == dp[idx]){
            idx = i;
            s.push(num[i]);
        }
    }
    cout<<"\n";
    while(!s.empty()){
        cout<<s.top()<<" ";
        s.pop();
    }
    return 0;
}

이 과정을 이해한다면 무려 플래티넘 5단계 문제를 정확히 이해하고 풀은 것이다!!

'코딩테스트 준비 > PS' 카테고리의 다른 글

C++ 백준 1707 (이분 그래프)  (0) 2021.09.14
C++ 백준 5430 (AC)  (0) 2021.09.08
C++ 백준 14002 (가장 긴 증가하는 부분수열4)  (0) 2021.09.04
C++ 백준 1562 (계단 수)  (0) 2021.09.03
C++ 백준 17404 (RGB거리 2)  (0) 2021.09.03