본문 바로가기
알고리즘/백준

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

by fortissimo 2024. 8. 1.

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

문제


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

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

 

입력


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

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

 

출력


첫째 줄에 수열 A의 가장 긴 증가하는 부분 수열의 길이를 출력한다.

둘째 줄에는 정답이 될 수 있는 가장 긴 증가하는 부분 수열을 출력한다.

 

문제 풀이


O(nlogn)으로 푸는 LIS 문제.

12738번의 가장 긴 증가하는 부분 수열3과 유사하지만, 정답이 되는 부분 수열까지 출력해야 한다. 12738번을 그대로 사용해서 해당 dp 벡터의 값을 출력하면 될 것 같지만, 그렇지 않다.

 

O(nlogn)으로 푸는 LIS 문제는 dp 벡터에 저장되는 숫자들은 인덱스 0은 길이 1로 얻을 수 있는 가장 작은 숫자, 인덱스 1은 길이 2로 얻을 수 있는 가장 작은 숫자..등의 형식으로 값이 저장되어 있다. 문제는 그 가장 작은 숫자를 저장할 때 원본 배열의 인덱스 순서까지 고려하지 않는다는 점이다. 따라서 저장되는 값은 다르지만, 그 길이는 LIS의 최대 길이가 된다.

실제 배열의 가장 긴 증가하는 부분 수열을 구하려면 역추적을 사용해야 한다. 추가로 크기 N짜리 indexes 배열을 만들어 dp 벡터를 구하면서 indexes[i]에 해당 값으로 얻을 수 있는 dp 벡터의 인덱스를 저장한다.

 

예시로 예제 입력 1을 보자.

10 20 10 30 20 50

처음인 10은 dp 벡터에 추가된다. 따라서 indexes[0]은 0이 된다.

20은 벡터의 마지막인 10보다 크므로 dp 벡터에 추가되고, indexes[1]은 1이 된다.

10은 동일하므로 indexes[2]는 0이 된다.

30은 벡터의 마지막인 20보다 크므로 dp 벡터에 추가되고, indexes[3]은 2가 된다.

20은 두번째 원소와 같으므로 indexes[4]는 1이 된다.

50은 벡터의 마지막인 30보다 크므로 dp 벡터에 추가되고, indexes[4]은 3가 된다.

 

dp 벡터를 통해 크기를 알고 있으므로 indexes를 끝부분부터 처음까지 역으로 탐색해 부분 수열을 얻어내면 된다.

 

아래는 코드.

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

int main()
{
	cin.tie(NULL);
	ios::sync_with_stdio(false);

	int N;
	cin >> N;
	int* arr = new int[N];
	int* indexes = new int[N];
	stack<int> answer;
	for (int i = 0;i < N; i++)
	{
		cin >> arr[i];
		indexes[i] = 0;
	}
	vector<int> v;
	indexes[0] = 0;
	v.push_back(arr[0]);
	for (int i = 1; i < N; i++)
	{
		if (arr[i] > v.back())
		{
			v.push_back(arr[i]);
			indexes[i] = v.size() - 1;
		}
		else
		{
			int index = lower_bound(v.begin(), v.end(), arr[i]) - v.begin();
			v[index] = arr[i];
			indexes[i] = index;
		}
	}
	int currentFindIndex = v.size() - 1;
	for (int i = N - 1; i >= 0; i--)
	{
		if (indexes[i] == currentFindIndex)
		{
			currentFindIndex--;
			answer.push(arr[i]);
		}
	}
	cout << v.size() << "\n";
	while (!answer.empty())
	{
		cout << answer.top() << " ";
		answer.pop();
	}
	return 0;
}