출처 : 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
고려사항
- dp를 활용한 lis 알고리즘을 사용하면 정답은 구할 수 있다. ( 백준 11053 문제 글 15 - 23 라인 참조)
- 이러한 2중 for문을 수열의 길이만큼 100만번씩 돌리면 당연히 시간초과.
자신보다 작으면서, 부분 수열의 길이가 제일 긴 memo값을 찾는 for문을 이분탐색 아니면 세그먼트 트리로 구하면 시간단축가능! - 그런데 더 간단한 방법을 발견 ( 구글링... )
- lower_bound( begin, end, num) 함수를 활용.
lower_bound( ) 는 vector 의 원소들 중에서 num보다 크거나 같은 첫 원소의 위치(iter)를 반환한다.
첫 원소이기 때문에, vector가 정렬되어 있다면, 원소 중 num 과 같은 수를 찾거나 없으면, num보다 큰 수들 중 가장 작은 수를 반환.
정렬 되어있지 않으면 num과 같은 수의 위치를 반환하거나, 없으면 큰 수들 중 처음으로 만나는 수의 위치를 반환한다.
위 문제에서는 vector에 담기는 수들이 문제의 조건을 만족하는 수들, 즉! 증가형태를 띄는 수열이기 때문에 이미 정렬되어서 차곡차곡 생성 될 것이다! - 입력 받은 수가 v.back() 즉, 부분 수열 중 가장 큰값보다 크면, push_back()
- 작다면, lower_bound() 를 통해 num이 들어갈 위치를 찾고, 교체해준다.
- 모든 수를 처리했을 때, vector의 사이즈가 가장 긴 부분 수열 길이이다.
- 단, 벡터에 담겨있는 수열이 옳은 수열이지는 않다! 단지 길이만 알 수 있다.
예를들어, 수열 [ 10, 20, 30, 15, 40 ] 의 정답 부분 수열은 [ 10, 20, 30, 40 ] 으로 길이가 4이지만,
위 알고리즘으로 문제를 풀면 [ 10, 20, 30 ] 형태에서 15를 처리해 줄 때,
lower_bound(15) => 20 위치 반환 => [ 10, 15, 30, 40 ]의 형태로 벡터가 구성된다.
당연히 15가 30 보다, 뒤에 등장하기에 문제 조건에 맞지 않는다! - 따라서 위 알고리즘은 단순히 가장 긴 증가하는 부분 수열의 길이만을 알 수 있다! ( 사실 dp를 활용한 lis 알고리즘도 마찬가지다 )
#include <iostream>
#include <vector>
using namespace std;
int main() {
cin.tie(NULL);
ios::sync_with_stdio(false);
int n, num;
vector<int> v(1,1000001);
cin>>n;
for(int i=0;i<n;++i){
cin>>num;
if(v.back() < num){
v.push_back(num);
}else{
auto iter = lower_bound(v.begin(), v.end(), num);
*iter = num;
}
}
cout<<v.size();
return 0;
}
'BOJ 백준 > 기타' 카테고리의 다른 글
| 백준 07662 - 이중 우선순위 큐 (0) | 2020.11.12 |
|---|---|
| 백준 02749 - 피보나치 수 3 (0) | 2020.11.11 |
| 백준 01717 - 집합의 표현 (0) | 2020.11.09 |
| 백준 05430 - AC (0) | 2020.11.05 |
| 백준 01021 - 회전하는 큐 (0) | 2020.11.05 |
