최장 증가 수열의 길이(LIS)
다음과 같은 수열이 있을 때 순서를 유지하면서 크기가 점진적으로 커지는 가장 긴 부분의 수열의 길이를 말함.
-> 3,2,6,4,5,1 일 경우는
2,4,5 | 3,5,6 => 즉, 3 이다.
이걸 구하는 알고리즘이 LIS이다.
Brute-force 방법으로 푼다면
길이가 0 일때 모든 부분집합.
1일 때 2일 때 ... 쭉 불가능 할 때 까지 가는 것이다.
이 경우 같은 경우 부분집합의 총 개수가 2^N이기 때문에 O(2^n)이 나오게 된다.
Dynamic programming(DP)
숫자열 - a,b,c,d,e ....
LIS[i] : a,b,c,d,e에서 가장 긴 부분 수열의 길이라 하면
1번 : LIS(i)가 i번째 원소를 포함하지 않는다면, LIS(i) = LIS(i-1)이다.
2번 : LIS(i)가 i번째 원소를 포함한다면?
i번째 원소보다 작은 원소를 그 전에 있던 원소에서 찾는다.
그 전에 있던 원소를 찾기 위해서는 모두 검색을 해야만 찾을 수 있음.
그 중 최대값을 찾아 1증가시켜서 LIS(i)에 저장해주면 된다.
시간 복잡도는 O(N^2)이 됩니다.
ex) {3,2,6,4,5,1}
dp(1) = 1
dp(2) = 1
dp(3) = {3,6} -> 2
dp(4) = {3,4} -> 2
dp(5) = {3,4,5} = 3
dp(6) = 1
코드로 푼다면 이런식이 될 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
int last = 0;
int len = 1;
int prev[10], dp[10];
for(int i =0; i<n; i++){
dp[i] = 1;
prev[i] = -1;
for(int j=0; j<i; j++){
if(a[j] > a[i])
continue;
if(dp[i] < dp[j]+1){
dp[i] = dp[j]+1;
prev[i] = j;
if(len < dp[i]){
last = i;
len = dp[i];
}
}
}
}
void PrintArray(int pos){
if(pos<0)
return;
PrintArray(prev[pos]);
cout << a[pos] << " ";
}
void PrintArray(int pos){
int idx = pos;
for(int i = 0; idx >= 0; i++){
ans[i] = idx;
idx = prev[idx];
}
for(int i = dp[n-1]-1; i>=0; i--)
cout << a[ans[i]] << " ";
}
|
cs |
prev는 출력을 위해 i번째 원소를 붙이기 이전의 index를 저장하고 있다.
출력하는 버전이 2개 있는데 재귀로 출력할 경우 stack overflow가 발생할 수 있기 때문에
되도록이면 for문을 이용하는 것이 좋다.
'알고리즘 > 알고리즘' 카테고리의 다른 글
DP - TSP(Traveling Salesman Problem) 최소 여행 경비 c++ [컴공과고씨] (0) | 2023.01.26 |
---|---|
다익스트라(Dijkstra) 간단 정리 [컴공과고씨] (0) | 2023.01.02 |
유니온 파인드(Union Find) C++ [컴공과고씨] (2) | 2022.09.14 |