가장 긴 감소하는 부분 수열 
https://www.acmicpc.net/problem/11722
시간 제한 | 메모리 제한 | 제출 | 정답 | 맞힌 사람 | 정답 비율 |
---|---|---|---|---|---|
1 초 | 256 MB | 43124 | 26527 | 21831 | 62.422% |
문제
수열 A가 주어졌을 때, 가장 긴 감소하는 부분 수열을 구하는 프로그램을 작성하시오.
예를 들어, 수열 A = {10, 30, 10, 20, 20, 10} 인 경우에 가장 긴 감소하는 부분 수열은 A = {10, 30, 10, 20, 20, 10} 이고, 길이는 3이다.
입력
첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000)이 주어진다.
둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000)
출력
첫째 줄에 수열 A의 가장 긴 감소하는 부분 수열의 길이를 출력한다.
예제 입력 1
6 10 30 10 20 20 10
예제 출력 1
3
예제 입력 2
5 5 5 5 5 5
예제 출력 2
1
예제 입력 3
6 10 9 8 7 6 5
예제 출력 3
6
예제 입력 3
8 50 40 30 60 20 10 70 5
예제 출력 3
5
출처
- 문제를 만든 사람: baekjoon
비슷한 문제
- 11053번. 가장 긴 증가하는 부분 수열
- 11054번. 가장 긴 바이토닉 부분 수열
- 11055번. 가장 큰 증가하는 부분 수열
- 12015번. 가장 긴 증가하는 부분 수열 2
- 12738번. 가장 긴 증가하는 부분 수열 3
- 14002번. 가장 긴 증가하는 부분 수열 4
- 14003번. 가장 긴 증가하는 부분 수열 5
알고리즘 분류
이진 탐색을 활용한 LDS (Longest Decreasing Subsequence, 최장 감소 부분 수열)
O(N log N)

#include <iostream> #include <vector> #include <algorithm> using namespace std; pair<int, vector<int>> FindLDS(const vector<int>& input) { int n = input.size(); vector<int> tail; // 각 길이별 LDS의 마지막 원소 인덱스 vector<int> prev_idx(n, -1); // 이전 원소의 인덱스 추적 for (int i = 0; i < n; i++) { int lo = 0; int hi = (int)tail.size() - 1; int pos = (int)tail.size(); while (lo <= hi) { int mid = (lo + hi) / 2; if (input[tail[mid]] > input[i]) { lo = mid + 1; } else { pos = mid; hi = mid - 1; } } if (pos > 0) { prev_idx[i] = tail[pos - 1]; } else { prev_idx[i] = -1; } if (pos == (int)tail.size()) { tail.push_back(i); } else { tail[pos] = i; } } vector<int> lds; for (int idx = tail.back(); idx != -1; idx = prev_idx[idx]) { lds.push_back(input[idx]); } reverse(lds.begin(), lds.end()); return { tail.size(), lds }; } int main() { ios::sync_with_stdio(false); std::cin.tie(NULL); std::cout.tie(NULL); int N; std::cin >> N; vector<int> input(N); for (int i = 0; i < N; i++) std::cin >> input[i]; auto lds = FindLDS(input); cout << lds.first; return 0; }
input = [9, 4, 3, 2, 5]
단계 | i | input[i] | tail 내용 (저장된 인덱스) | 의미 |
---|---|---|---|---|
0 | 0 | 9 | [0] | 길이 1 LDS → 끝 원소는 input[0] = 9 |
1 | 1 | 4 | [0, 1] | 길이 2 LDS → 끝 원소는 input[1] = 4 |
2 | 2 | 3 | [0, 1, 2] | 길이 3 LDS → 끝 원소는 input[2] = 3 |
3 | 3 | 2 | [0, 1, 2, 3] | 길이 4 LDS → 끝 원소는 input[3] = 2 |
4 | 4 | 5 | [0, 4, 2, 3] | 길이 1~4까지의 각 LDS의 끝 인덱스 갱신 |
input = [10, 8, 6, 5, 7, 4, 3, 2, 9]
단계 | i | input[i] | 이진 탐색 결과 pos | tail 내용 (저장된 인덱스) | tail 에 대응되는 실제 값 | prev_idx[i] | 동작 설명 |
---|---|---|---|---|---|---|---|
0 | 0 | 10 | 0 | [0] | [10] | -1 | 첫 원소 → 길이 1 LDS 시작 |
1 | 1 | 8 | 1 | [0, 1] | [10, 8] | 0 | 10→8 감소, 길이 2 LDS |
2 | 2 | 6 | 2 | [0, 1, 2] | [10, 8, 6] | 1 | 8→6 감소, 길이 3 LDS |
3 | 3 | 5 | 3 | [0, 1, 2, 3] | [10, 8, 6, 5] | 2 | 6→5 감소, 길이 4 LDS |
4 | 4 | 7 | 2 | [0, 1, 4, 3] | [10, 8, 7, 5] | 1 | 8→7으로 갱신 (길이 3의 마지막 갱신) |
5 | 5 | 4 | 4 | [0, 1, 4, 3, 5] | [10, 8, 7, 5, 4] | 3 | 5→4로 확장 |
6 | 6 | 3 | 5 | [0, 1, 4, 3, 5, 6] | [10, 8, 7, 5, 4, 3] | 5 | 4→3 감소 확장 |
7 | 7 | 2 | 6 | [0, 1, 4, 3, 5, 6, 7] | [10, 8, 7, 5, 4, 3, 2] | 6 | 3→2 감소 확장 |
8 | 8 | 9 | 1 | [0, 8, 4, 3, 5, 6, 7] | [10, 9, 7, 5, 4, 3, 2] | 0 | 10→9으로 교체 (2번째 LDS 끝 갱신) |
항목 | 의미 |
---|---|
tail.size() | 7 → 최장 감소 부분 수열의 길이 = 7 |
tail.back() | 7 → input[7] = 2 → LDS의 마지막 원소 |
prev_idx | [ -1, 0, 1, 2, 1, 3, 5, 6, 0 ] → 각 원소가 연결된 이전 원소 인덱스 |