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
풀이
이 문제는 이전 글에서 다루었던 가장 긴 증가하는 부분 수열(https://shyeon.tistory.com/52)에서 사용한 DP를 이용할 경우 시간 복잡도가 O(n^2)이 걸리므로 이 문제의 최대 길이인 10^6이 주어질 경우 10^12으로 시간제한인 1초를 훌쩍 넘게 된다. 따라서 이 문제는 O(N * log N)이 걸리는 알고리즘 방식을 이용해야한다.
이 알고리즘은 가장 현재까지 탐색한 수열 중 가장 긴 수열의 길이를 가진 배열을 항상 유지한다. 만약 배열의 맨 마지막 원소 즉 가장 큰 값보다 클 경우 배열 맨 뒤에 붙여준다. 그렇지 않을 경우 현재 위치의 원소가 그 배열에서 위치할 수 있는 곳을 이진탐색을 이용하여 찾고 그 위치의 원소와 바꾼다. 예시를 이용하여 다시 설명하겠다.
예제
위 예제를 풀이한 알고리즘 순서대로 풀어보겠다. 처음 원소는 음수의 무한대로 초기화한다. 따라서
- -INF
- -INF 10 (맨 끝에 추가)
- -INF 10 20 (맨 끝에 추가)
- -INF 10 20 (10자리에 10이 들어갈 수 있음)
- -INF 10 20 30 (맨 끝에 추가)
- -INF 10 20 30 (20 자리에 10 교체)
- -INF 10 20 30 50 (맨 끝에 추가)
위의 예시는 반복되는 숫자가 있으므로 다른 예시로 한 번 더 해보겠다.
예시 : 10 20 40 30 70 90 50 60 80
- -INF
- -INF 10
- -INF 10 20
- -INF 10 20 40
- -INF 10 30 40
- -INF 10 30 40 70
- -INF 10 30 40 70 90
- -INF 10 30 40 50 90
- -INF 10 30 40 50 60
- -INF 10 30 40 50 60 80
이런 식으로 진행된다. 따라서 마지막 크기에서 -1을 해주면 가장 긴 수열의 길이를 구할 수 있다. 마지막에는 가장 긴 수열이 저장되는 것은 아니지만 길이는 이 방법을 이용하여 O(N log N)으로 구할 수 있다.
소스 코드
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MN = 1001;
const int INF = 1e9; //무한대
vector<int> dp;
int main() {
int N; cin >> N;
dp.push_back(-INF); //마이너스 무한대 push
for(int i = 0; i < N; i++) {
int a; cin >> a; //입력 받아서
if(dp.back() < a) { //배열의 맨 마지막 원소보다 크면 뒤에 저장
dp.push_back(a);
}
else { //그렇지 않을 경우
auto it = lower_bound(dp.begin(), dp.end(), a); // 이진탐색을 이용하여 들어갈 수 있는 위치 탐색
*it = a; //해당 위치에 교체
}
}
cout << dp.size() - 1; // -무한대 빼고 길이 출력
}
채점 결과
'알고리즘 > 백준' 카테고리의 다른 글
[C++] 백준 3190번 뱀 풀이 (0) | 2021.08.21 |
---|---|
[C++] 백준 2565번 전깃줄 풀이 (0) | 2021.08.17 |
[C++] 백준 11053번 가장 긴 증가하는 부분 수열 풀이 (0) | 2021.08.13 |
[C++] 백준 2110번 공유기 설치 풀이 (0) | 2021.08.08 |
[C++] 백준 13460번 구슬 탈출 2 풀이 (0) | 2021.08.07 |