-
Notifications
You must be signed in to change notification settings - Fork 4
LIS
Changi Cho edited this page Dec 21, 2021
·
7 revisions
최장 증가 수열
보통 LIS를 구하는 문제의 답은 한 수열에서 주어지는 LIS의 길이가 답이 된다.
index를 순차적으로 증가시켜가며, 이전에 방문했던 0 ~ (index-1)까지의 값을 다시 순회하며 계산했던 값을 이용해 현재 index의 값을 갱신한다.
int lis(vector<int> array) {
int size = array.size();
// initialize
vector<int> dp(size, 1);
for (int target = 0; target < size; target++) {
for (int before = 0; before < target; before++) {
if (array[before] < array[target]) {
dp[target] = max(dp[target], dp[before] + 1);
}
}
}
return dp.back();
}시간 복잡도 : O(N * log_2(N))
- N : O(N)으로 배열을 탐색함
- log_2(N) : lower_bound로 최적의 자리를 찾음
배열을 순회하면서 벡터의 맨 뒤 원소와 현재 보고있는 수열의 원소를 비교한다.
수열의 원소가 더 클 시 벡터에 push_back해준 뒤 LIS의 크기(답)을 1증가 시킨다. 수열의 원소가 벡터의 맨 뒤 원소보다 작을 경우 lower_bound를 이용하여 최적의 자리를 찾은 뒤 그 자리의 값을 해당 수열의 원소로 교체한다.
LIS 배열을 구하기 위해선 각 index별로 어떤 값이 있는지 같이 저장해야한다.
int lisLength(vector<int>& nums) {
int size = nums.size();
vector<int> lis;
for (int i = 0; i < size; i++) {
int target = lower_bound(lis.begin(), lis.end(), nums[i]) - lis.begin();
if (target == lis.size()) {
lis.push_back(nums[i]);
} else {
lis[target] = nums[i];
}
}
// lis 배열에 LIS를 만들기 위해 임시로 사용된 숫자들이 들어있음
return lis.size();
}LIS 알고리즘에서 lower_bound를 이용해 이전에 나온 값중에서 현재값 이상인 부분을 교채한다.
이를 upper_bound를 이용할 경우 이전에 나온 값중 현재값을 초과하는 부분을 교채하며, 이 과정에서 중복해서 원소가 들어갈 수 있다.
int longestNonDecreasingSubsequence(vector<int> &nums){
int size = nums.size();
vector<int> lis;
for (int i = 0; i < size; i++) {
int target = upper_bound(lis.begin(), lis.end(), nums[i]) - lis.begin();
if (target == lis.size()) {
lis.push_back(nums[i]);
} else {
lis[target] = nums[i];
}
}
// lis 배열에 LIS를 구성하는 원소가 들어있음
return lis.size();
}struct LIS {
int index, from;
};
vector<int> longestIncreasingSubsequence(int N, vector<int> array) {
vector<LIS> lis;
int lis_cache[MAX] = {
0,
};
int index = 0;
lis_cache[index] = array[0];
lis.push_back({0, array[0]});
for (int i = 1; i < N; i++) {
if (lis_cache[index] < array[i]) {
index++;
lis_cache[index] = array[i];
lis.push_back({index, array[i]});
} else {
int new_index = lower_bound(lis_cache, lis_cache + index, array[i]) - lis_cache;
lis_cache[new_index] = array[i];
lis.push_back({new_index, array[i]});
}
}
// index + 1은 LIS 배열의 크기를 의미한다.
// 역순으로 LIS 배열을 구한다. (index를 이용해 역순으로 구함)
stack<int> s;
for (int i = N - 1; i >= 0; i--) {
if (lis[i].index == index) {
s.push(lis[i].from);
index--;
}
}
// stack에 역순으로 저장되어있으므로 뒤집어 LIS 배열을 구함
vector<int> ret;
while (!s.empty()) {
ret.push_back(s.top());
s.pop();
}
return ret;
}