본문 바로가기
알고리즘/BAEKJOON

BOJ 12015 : 가장 긴 증가하는 부분 수열 2

by 피로물든딸기 2024. 12. 20.
반응형

알고리즘 문제 전체 링크

삼성 B형 전체 링크

 

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

 

참고

- BOJ 1655 : 가운데를 말해요 with 세그먼트 트리

 

 

수열 A의 크기는 1,000,000이고 들어 있는 값은 1부터 1,000,000이므로 세그먼트 트리를 이용해서 문제를 풀 수 있다.

 

예제의 {10, 20, 10, 30, 20, 50}에서

가장 긴 증가하는 부분 수열(LIS, Longest Increasing Subsequence)의 길이 4를 찾아보자.

 

수열에 값이 하나가 추가될 때마다, LIS의 길이세그먼트 트리를 이용해서 갱신하면 문제를 풀 수 있다.

x가 추가되고, 구간 [1, x - 1]의 LIS의 길이y라면 구간 [1, x]LIS의 길이y + 1이 된다.

이때 세그먼트 트리의 [x, x]y + 1을 갱신하는 방식으로 LIS의 길이를 구할 수 있다.

 

수열의 첫 번째는 10이기 때문에 세그먼트 트리의 10에 해당하는 구간을 업데이트해야 한다.

10이 추가 될 때 LIS의 길이는 세그먼트 트리에서

구간 [1, 10 - 1]에서 LIS의 길이1을 더하면 된다.

하지만, 10은 첫 번째로 들어온 숫자이기 때문에 [1, 10 - 1] = [1, 9] 구간에서 LIS가 없으므로, LIS의 길이0이 된다.

즉, [1, 10]에서 LIS의 길이는 0 + 1이 되고, 이 값을 구간 [10, 10]1로 세그먼트 트리에 업데이트한다.

 

수열의 두 번째는 20이다.

[1, 20 - 1]에서 LIS의 길이[10, 10] = 1이 된다.

즉, 20이 추가되면서 {10} → {10, 20} 의 LIS의 길이1 + 1이 된다.

따라서, 구간 [20, 20]2로 세그먼트 트리에 업데이트한다.

 

수열의 세 번째는 다시 10이다.

구간 [1, 10 - 1]에서 LIS의 길이는 여전히 0이다.

따라서, 구간 [10, 10]0 + 1로 다시 업데이트면 된다.

 

수열의 네 번째는 30이다.

구간 [1, 30 - 1]에서 가장 큰 값은 구간 [20, 20]에 의해 2가 된다.

즉, {10, 20, 10}에 30을 추가하면 LIS의 길이2 + 1이 된다.

따라서, 구간 [30, 30]3을 세그먼트 트리에 업데이트한다.

 

수열의 다섯 번째는 20이다.

구간 [1, 20 - 1]에서 가장 큰 값은 [10, 10]에 의해 1이 된다.

따라서, 구간 [20, 20] 1 + 1을 다시 업데이트한다.

 

수열의 마지막은 50이다.

구간 [1, 50 - 1]에서 가장 큰 값은 구간 [30, 30]에 의해 3이 된다.

따라서, 구간 [50, 50]3 + 1을 세그먼트 트리에 업데이트한다.

 

수열로 주어지는 숫자는 값이 최대 1,000,000이므로,

[1, 1,000,000] 에서 가장 큰 값은 [50, 50]에 의해 4가 된다.


세그먼트 트리update를 이용해서 구간에서 LIS의 길이를 갱신한다.

void update(int left, int right, int node, int index, int value)
{
	if (index < left || right < index) return;

	if (left == right)
	{
		tree[node] = value;
		return;
	}

	int mid = (left + right) / 2;
	int leftNodeIndex = node * 2;
	int rightNodeIndex = node * 2 + 1;

	update(left, mid, leftNodeIndex, index, value);
	update(mid + 1, right, rightNodeIndex, index, value);

	int leftValue = tree[leftNodeIndex];
	int rightValue = tree[rightNodeIndex];

	tree[node] = (leftValue > rightValue) ? leftValue : rightValue;
}

 

그리고 주어진 구간 [a, b] 내에 가장 큰 값은 getMax를 이용해서 찾을 수 있다.

즉, [a, b]에서 LIS의 길이를 찾는다.

int getMax(int left, int right, int a, int b, int node)
{
	if (b < left || right < a) return 0;
	if (a <= left && right <= b) return tree[node];

	int mid = (left + right) / 2;

	int leftValue = getMax(left, mid, a, b, node * 2);
	int rightValue = getMax(mid + 1, right, a, b, node * 2 + 1);

	return (leftValue > rightValue) ? leftValue : rightValue;
}

 

value가 구간에 추가된다면,

getMax를 이용해 전체 구간 [1, 1,000,000][1, value -1]에서 LIS의 길이를 찾는다.

int max = getMax(1, 1000000, 1, value - 1, 1);

 

구간 [1, value - 1] 까지 LIS의 길이max가 되기 때문에,

value가 추가되면 value를 포함하는 구간 [1, value]LIS의 길이 max + 1이 된다.

update(1, 1000000, 1, value, max + 1);

 

모든 수열에 대해 LIS의 길이를 갱신하고, 마지막에 전체 구간LIS의 길이를 출력하면 된다.

printf("%d\n", getMax(1, 1000000, 1, 1000000, 1));

 

전체 코드는 다음과 같다.

#include <stdio.h>

#define MAX (50000 + 500000)

int N;
int tree[4004000];

void update(int left, int right, int node, int index, int value)
{
	if (index < left || right < index) return;

	if (left == right)
	{
		tree[node] = value;
		return;
	}

	int mid = (left + right) / 2;
	int leftNodeIndex = node * 2;
	int rightNodeIndex = node * 2 + 1;

	update(left, mid, leftNodeIndex, index, value);
	update(mid + 1, right, rightNodeIndex, index, value);

	int leftValue = tree[leftNodeIndex];
	int rightValue = tree[rightNodeIndex];

	tree[node] = (leftValue > rightValue) ? leftValue : rightValue;
}

int getMax(int left, int right, int a, int b, int node)
{
	if (b < left || right < a) return 0;
	if (a <= left && right <= b) return tree[node];

	int mid = (left + right) / 2;

	int leftValue = getMax(left, mid, a, b, node * 2);
	int rightValue = getMax(mid + 1, right, a, b, node * 2 + 1);

	return (leftValue > rightValue) ? leftValue : rightValue;
}

int main()
{
	scanf("%d", &N);

	for (int i = 0; i < N; i++)
	{
		int value;

		scanf("%d", &value);

		int max = getMax(1, 1000000, 1, value - 1, 1);
		update(1, 1000000, 1, value, max + 1);
	}

	printf("%d\n", getMax(1, 1000000, 1, 1000000, 1));

	return 0;
}
반응형

'알고리즘 > BAEKJOON' 카테고리의 다른 글

BOJ 2338 : 긴자리 계산  (0) 2023.07.29
BOJ 10757 : 큰 수 A+B  (0) 2023.07.29
BOJ 7453 : 합이 0인 네 정수  (0) 2023.04.07
BOJ 1991 : 트리 순회  (0) 2023.04.07
BOJ 6593 : 상범 빌딩  (0) 2023.03.26

댓글