<문제링크>: https://www.acmicpc.net/problem/14003
DP를 위한 메모용 벡터 vector<int> memo
를 생성합니다.
사용자로부터 입력받는 수열 vec
과 memo
는 모두 1번 인덱스부터 사용합시다.
vec
에 다음과 같은 입력이 들어왔다고 가정합니다.
vec | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
- | 4 | 9 | 11 | 5 | 7 | 8 |
- | 0 | 0 | 0 | 0 | 0 | 0 |
눈으로 보고 알 수 있는 LIS는 LIS: 4, 5, 7, 8
입니다.
memo | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
- | 4 | 9 | 11 |
우선 4
가 삽입됩니다. 4
보다 9
가 크므로 의심없이 삽입합니다.
9
보다 11
이 더 크므로 마찬가지로 의심없이 삽입합니다. 이때 vec
의 각 요소의 second
에는 memo
에서의 인덱스가 저장되도록 합니다.
memo | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
- | 4 | 5 | 11 |
5
는 11
보다 작은 수입니다. 따라서 그냥 삽입해서는 안됩니다. algorithm
헤더파일에 있는 lower_bound
함수를 이용해서 5
가 들어갈 적절한 인덱스를 찾고 값을 갱신합니다. 따라서 9
가 5
로 갱신됩니다.
memo | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
- | 4 | 5 | 7 | 8 |
vec
의 마지막 인덱스까지 순회가 종료되면 memo
는 위 표와 같습니다.
vec | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
- | 4 | 9 | 11 | 5 | 7 | 8 |
- | 1 | 2 | 3 | 2 | 3 | 4 |
vec
의 마지막 인덱스까지 순회가 종료되면vec
은 위 표와 같습니다.
이제 vec
의 마지막 인덱스부터 거꾸로 1번 인덱스까지 순회하면서 LIS를 만들어갑니다. 이때 우리는 stack<int>
를 사용해서 역순 출력할 것입니다.
stack | 0 | 1 | 2 | 3(=top) |
---|---|---|---|---|
- | 8 | 7 | 5 | 4 |
순회가 종료되면 스택은 위와 같은 구조를 보입니다. 이제 std::cout << stack.top() << " "
과 stack.pop()
을 반복하며 역순 출력합니다.
반례 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
문제 | 4 4 1 2 3 | 4 1 3 4 2 | 6 4 5 6 1 2 3 | 6 4 9 11 5 7 8 | 8 1 6 2 5 7 3 5 6 | 9 3 1 2 4 7 5 6 8 10 |
답 | 3 1 2 3 | 3 1 3 4 | 3 1 2 3 | 4 4 5 7 8 | 5 1 2 3 5 6 | 7 1 2 4 5 6 8 10 |
설령 본인 코드가 AC를 받았더라도 반드시 위 반례를 돌려봅시다.
#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
static int N;
static vector<pair<int, int>> vec; // 사용자 입력 수열
static stack<int> stk; // 출력하기 위한 스택
static vector<int> memo; // DP용 메모 배열
void memoization() {
for (int i = 1; i <= N; ++i) {
if (memo.back() < vec[i].first) { // [분기 1]-LIS 후보 요소 발견
memo.push_back(vec[i].first); // Memo 벡터에 삽입
vec[i].second = memo.size(); // vec 벡터 second에 Memo 상의 인덱스값 명시
} else { // [분기 2]-Memo 벡터 갱신 요소 발견
auto itr = lower_bound(begin(memo), end(memo), vec[i].first);
*itr = vec[i].first; // Memo 벡터의 해당 요소를 작은 값으로 갱신
vec[i].second = itr - begin(memo) + 1; // vec 벡터 second에 Memo 상의 인덱스값 명시
}
}
}
void printLIS() {
int memoIdx = memo.size();
for (int i = N; i > 0; --i) {
if (vec[i].second == memoIdx) {
stk.push(vec[i].first);
memoIdx--;
}
}
cout << stk.size() << '\n';
while (!stk.empty()) {
cout << stk.top() << " ";
stk.pop();
} cout << '\n';
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> N;
vec.resize(N + 1);
for (int i = 1; i <= N; ++i) { cin >> vec[i].first; vec[i].second = i; }
memo.push_back(vec[1].first); // 초기값
memoization();
printLIS();
}
memo
를 사용해서 개수까지는 잘 출력할 수 있었지만, LIS 수열을 만드는 과정을 자꾸 실수해서 오답이 발생했다.stack
같은 기본 자료구조를 잘 활용할 수 있어야겠다.lower_bound
같은 STL function을 최대한 활용해보자.