2 분 소요

문제Permalink

수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오.

예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 30, 50} 이고, 길이는 4이다.

입력Permalink

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다.

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

예제 입력 1Permalink

6
10 20 10 30 20 50

출력Permalink

첫째 줄에 수열 A의 가장 긴 증가하는 부분 수열의 길이를 출력한다. 둘째 줄에는 가장 긴 증가하는 부분 수열을 출력한다. 그러한 수열이 여러가지인 경우 아무거나 출력한다.

예제 출력 1Permalink

4
10 20 30 50

풀이Permalink

문제 해결 방법Permalink

이 문제는 LIS(Longest Increasing Subsequence) 알고리즘을 최적화하여 해결할 수 있습니다. 주요 해결 단계는 다음과 같습니다:

  1. 이분 탐색을 이용한 LIS:
    • 현재까지의 증가 수열을 유지하면서 새로운 원소를 적절한 위치에 삽입
    • 세그먼트 트리를 이용하여 최적화
  2. 경로 복원:
    • 각 원소의 위치를 저장하여 실제 수열을 역추적
    • 마지막 원소부터 시작하여 LIS를 구성

이 알고리즘의 시간 복잡도는 O(N log N)입니다.

코드Permalink

#include<stdio.h>
#include<algorithm>

using namespace std;

int n, nn;
int idx[2100000];
int path[1000001];
int dp[1000001];
int a[1000001];
pair<int, int> st[1000001];

int get_max(int l, int r)
{
    l += nn - 1;
    r += nn - 1;

    int mx = 0;
    while (l <= r)
    {
        if (l & 1)mx = max(mx, idx[l++]);
        if (~r & 1)mx = max(mx, idx[r--]);

        l >>= 1;
        r >>= 1;
    }

    return mx;
}

void edit(int i, int v)
{
    i += nn - 1;
    idx[i] = v;
    i >>= 1;
    while (i)
    {
        idx[i] = max(idx[i * 2], idx[i * 2 + 1]);
        i >>= 1;
    }
}

int main()
{
    int i;
    scanf("%d", &n);
    for (i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        // a[i] 를 기준으로 오름차순 정렬. a[i]가 같다면 i기준 내림차순 정렬
        st[i] = { a[i],-i };
    }

    ////기존 방식////
    /*
    int j, k;
    for (i = 1; i <= n; i++)
    { 
        k = 0;
        for (j = 1; j < i; j++)
        {
            if (a[j] < a[i])
                k = max(k, dp[j]);
        }

        dp[i] = k + 1;
    }
    */ 
    /////////////////

    //index tree 셋팅 (초기화는 필요 x)
    for (nn = 1; nn < n; nn *= 2);

    // 정렬
    sort(st + 1, st + n + 1);

    int mx = 0, x;
    for (i = 1; i <= n; i++)
    {
        // i번째 데이터의 index
        x = -st[i].second;
        // dp[x] = 1 ~ x-1 구간의 최댓값 +1
        dp[x] = get_max(1, x - 1) + 1;
        // dp값을 index tree에 업데이트
        edit(x, dp[x]);

        // 최댓값 저장
        mx = max(dp[x], mx);
    }

    printf("%d\n", mx);
    int mxx = mx;
    int inf = 1e9;
    for (i = n; i >= 1; i--)
    {
        if (dp[i] == mxx && a[i] < inf)
        {
            path[mxx] = i;
            inf = a[i];
            mxx--;
        }
    }

    for (i = 1; i <= mx; i++)
        printf("%d ", a[path[i]]);
    return 0;
}

댓글남기기