728x90

 0. [c++] 백준


https://www.acmicpc.net/problem/12015


 1. 풀이


1) 동적계획법을 활용한 풀이

우선 이 풀이는 O(N^2)의 풀이이다.

풀이 방법은 다음과 같다.

1. cache라는 배열은 cache[i] = i번째 수를 기준으로 만들 수 있는 LIS(Longest Increasing Subsequence, 가장 긴 증가하는 부분 수열)의 길이를 저장한다.

2. 이제 모든 경우에 대해서 탐색을 수행한다. 이때, 동적 계획법을 적용해준다.

3. 이 탐색의 횟수를 줄여주기 위해서 1번에서 설명한 cache를 활용해 메모이제이션을 적용한다.

이미 LIS를 탐색한 후라면 그 수는 더이상 탐색하지 않고 바로 반환을 하도록 해준다.

4. 이 과정을 반복하며 최종적으로 LIS를 구할 수 있게 된다.


하지만, 이와 같은 풀이는 N = 10^6 인 지금의 문제에서는 시간초과가 발생한다.

이를 막기 위해서 다른 방법이 필요할 것으로 보인다.


2) 이분 탐색을 활용한 풀이

그러면 어떤 방법을 적용해야 시간복잡도를 줄일 수 있을까?

사실 아이디어가 잘 나지 않아서 다른 블로그를 찾아보았는데 똑같은 메모이제이션을 활용하지만, 저장하는 데이터를 다르게 구현하는 것이었다.

포인트는 각 배열의 마지막 수를 저장해주는 것이었다.

이 마지막 수 메모이제이션은 vector<int> sequence를 활용해주었고

sequence[i] = 길이가 (i-1)인 배열에서 가장 큰 수

를 저장해주었다.



예를 들어보자.

10, 20 ,30, 25, 15, 60 이라는 배열이 문제로 주어진 경우이다. 이제 LIS를 탐색하는 과정은 아래와 같다.

1. sequence[0]에 10이 저장된다.

2. sequence[0](=10) < 20 이므로, sequence[1]에 20이 저장된다.

3. sequence[1](=20) < 30 이므로, sequence[2]에 30이 저장된다.

3. sequence[2](=30) > 25 이므로, 부분 수열의 길이를 증가시키는 것은 불가능하고, sequence[2]에 저장되어있던 30을 25로 치환해준다.

(길이가 3인 부분 수열중 가장 큰 수가 30에서 25로 변경된다.)

4. sequence[3](=25) > 15 이므로, 부분 수열의 길이를 증가시키는 것은 불가능하고, sequence[1]에 저장되어있는 20을 15로 치환한다.

5. sequence[3](=25) < 60 이므로, sequence[4]에 60이 저장된다.


최종적으로 sequence는 10, 15, 25, 60을 저장하게 된다.

그리고 이때 sequence의 크기가 LIS의 크기와 동일하게 된다.


이러한 과정을 아래 소스코드에 구현을 할 수 있었고, 탐색에 소요되는 시간을 줄이기 위해 이분 탐색을 적용해주었다.




 2. 소스코드


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
37
38
39
#include<iostream>
#include<algorithm>
#include<vector>
 
 
using namespace std;
 
int cache[1000001];
int arr[1000001];
int N;
 
int dp(int here) {
    int& ret = cache[here];
    if (ret != 0)
        return ret;
    ret = 1;
    for (int next = here + 1; next < N; next++) {
        if (arr[here] < arr[next])
            ret = max(ret, dp(next) + 1);
    }
 
    return ret;
}
 
int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
 
    cin >> N;
    int ret = 0;
    for (int i = 0; i < N; i++) {
        cin >> arr[i];
    }
    for (int i = 0; i < N; i++) {
        ret = max(ret, dp(i));
    }
    cout << ret;
    return 0;
}
cs





2. 반복문과 이분 탐색을 활용한 풀이

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<iostream>
#include<algorithm>
#include<vector>
 
using namespace std;
 
int arr[1000001];
vector<int> sequence;
int N;
 
void binary_search(int num) {
    int low = 0, high = sequence.size()-1, mid;
    int ret = 1000000007;
    while (low <= high) {
        mid = (low + high) / 2;
        int here_num = sequence[mid];
        if (here_num >= num) {
            if (ret > mid)
                ret = mid;
            high = mid - 1;
        }
        else
            low = mid + 1;
    }
 
    sequence[ret] = num;
}
 
void find_lis() {
    sequence.push_back(arr[0]);
    for (int i = 1;i < N;i++) {
        if (sequence.back() < arr[i]) {
            sequence.push_back(arr[i]);
        }
        else {
            binary_search(arr[i]);
        }
    }
}
 
int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
 
    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> arr[i];
    }
 
    find_lis();
 
    cout << sequence.size();
 
    return 0;
}
cs





 3. 참고


https://seungkwan.tistory.com/8


질문이나 지적 있으시면 댓글로 남겨주세요~

도움 되셨으면 하트 꾹!


+ Recent posts