문제
https://www.acmicpc.net/problem/12015
12015번: 가장 긴 증가하는 부분 수열 2
첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000,000)
www.acmicpc.net
문제 요약
수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열의 길이를 출력한다.
ex) 수열 A = {10, 20, 10, 30, 20, 50} 인 경우, 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50}이다.
코드
#include<iostream>
#include<vector>
using namespace std;
int n;
vector<int> arr, res;
// res에서 key보다 큰 수 중 가장 먼저 나온 수의 인덱스 반환 (이진 탐색)
// 같은 수가 있을 경우 그 자리 반환
int mylower_bound(vector<int>& res, int key) {
int left = 0;
int right = res.size() - 1;
int ans = right;
while (left <= right) {
int mid = (left + right) / 2;
if (key <= res[mid]) {
right = mid - 1;
ans = mid;
}
else {
left = mid + 1;
}
}
return ans;
}
int main() {
// 입출력 시간 단축
ios_base::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
cin >> n;
arr.resize(n + 1);
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
res.push_back(arr[0]); // 초기 res 배열 상태
// 주어진 배열의 왼쪽 요소부터 체크
for (int i = 1; i < n; i++) {
// res의 마지막 요소보다 더 크다면,
if (res.back() < arr[i]) {
res.push_back(arr[i]); // res 맨 뒤에 삽입
}
else {
// res에서 가장 먼저 등장하는 arr[i]보다 큰 수의 인덱스
int idx = mylower_bound(res, arr[i]);
res[idx] = arr[i]; //
}
}
cout << res.size();
return 0;
}
코드 설명
같은 유형의 다른 LIS 문제와는 푸는 방식이 다르다.
기존 LIS 문제들은 2번의 for문으로 O(N^2)에 풀었지만, 이 문제는 N <= 1000000 이므로 불가능하다.
따라서 이진 탐색을 이용한다.
1. 먼저 가장 긴 부분 수열 정보를 저장할 벡터 res를 생성하고, 초기 상태로 A[0] 요소를 삽입한다.
+ res는 오름차순으로 저장되며, res의 길이는 가장 긴 부분 수열의 길이를 나타낸다.
2. 주어진 수열 A의 왼쪽 요소부터 하나씩 체크하면서, res의 마지막 요소 즉, 현재 res의 가장 큰 값보다 더 큰 값이라면, res에 맨 뒤에 삽입한다.
3. 그렇지 않고 res 맨뒤에 삽입이 불가하다면, A[i]보다 큰 값 중 가장 작은 값, 또는 같은 값이 있다면 그 자리에 있는 값을 A[i]로 변경한다.
+ res에서 A[i]보다 큰 값 중 가장 작은 값, 또는 같은 값의 위치를 이진 탐색으로 O(logN)에 찾는다.
4. res의 크기를 출력한다.
+ res는 가장 긴 부분 수열이 아님에 주의해야 한다.
'Algorithm Problems > 이진 탐색' 카테고리의 다른 글
[백준/C++] 1072번: 게임 (1) | 2024.04.27 |
---|---|
[백준/C++] 14003: 가장 긴 증가하는 부분 수열 5 (2) | 2024.02.04 |
[백준/C++] 6236번: 용돈 관리 (1) | 2024.02.01 |
[백준/C++] 17179번: 케이크 자르기 (1) | 2024.01.31 |
[백준/Python] 2110번: 공유기 설치 (2) | 2023.11.05 |