[Baekjoon,12015] 가장 긴 증가하는 부분 수열 2

1 분 소요

DP로는 안된다.

주어진 수열에서 가장 긴 증가하는 부분 수열의 길이를 구하는 문제이다. 이 문제는 수열의 길이가 최대 1,000,000 이므로 백준 11053번 문제의 해결 방법이었던 DP로는 해결할 수 없다.

// 이 방식은 사용할 수 없다. 
int len, seq[MAX], cache[MAX];
int LIS(int start)
{
    int& ret=cache[start+1];
    if(ret!=-1)
        return ret;
    ret=1;
    for(int next=start+1;next<len;++next)
        if(start==-1 || seq[start]<seq[next])
            ret=max(ret,LIS(next)+1);
    return ret;
}
int main(void)
{
    cin>>len;
    for(int i=0;i<len;++i)
    {
        cin>>seq[i];
        cache[i]=-1;
    }
    cache[len]=-1;
    cout<<LIS(-1)-1;
    return 0;
}

하나씩 추가하며 이분 탐색하자

해결 방법은 다음과 같은 방법으로 이분 탐색을 활용하는 것이다. 먼저 주어진 값들을 하나씩 수열에 추가하며 이 값이 수열의 제일 큰 값보다 크면 수열에 추가한다. 그리고 개수를 카운트한다. 만약 비교했을 때 주어진 값이 수열의 제일 큰 값보다 작다면 수열에서 같거나 큰 제일 왼쪽 값, 즉 lower_bound()를 이용하여 찾은 값을 주어진 값으로 바꾼다. 이때는 개수를 카운트 하지 않는다.

이렇게 하면 수열은 계속해서 정렬되어 있는 상태이며 중간에 삽입되어야 하는 값들이 나오면 lower_bound()를 이용하여 삽입한다. 삽입해야 할 때는 수열의 최대 값 보다 작은 값이 주어진 경우이다. 수열은 LIS가 아니지만 길이는 LIS의 길이가 맞다.

구현

구현 시 음의 무한대 값을 수열에 추가하고 시작하는 것을 볼 수 있다.

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

const int NEG_INF=-987654321;

int main()
{
    int len, ret=0;;
    cin>>len;
    
    vector<int> perm;
    perm.push_back(NEG_INF);
    
    for(int i=0;i<len;++i)
    {
        int n;
        scanf("%d",&n);
        if(perm.back()<n)
        {
            perm.push_back(n);
            ++ret;
        }
        else
        {
            vector<int>::iterator it=lower_bound(perm.begin(), perm.end(), n);
            *it=n;
        }
    }
    cout<<ret<<endl;
    return 0;
}

댓글남기기