[백준 30804번] 과일 탕후루

문제 링크

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

 

문제 요약

1부터 9로 이루어진 수열이 주어진다. 이때, 연속된 부분 수열 중에서 숫자가 최대 두 개의 숫자로만 이루어진 부분 수열의 최대 길이를 구하는 문제이다.

 

문제 풀이

이 문제를 푸는 가장 효율적인 방법은 슬라이딩 윈도우 기법을 이용하는 것이다. 하지만 문제에서 주어진 제약 조건들을 잘 활용하면 조금은 더 간단한 방법으로 문제를 해결할 수 있다. 이 문제가 Division 1, Division 2에 동시에 출제된 것을 보면 다양한 접근 방법을 생각할 수 있게 의도된 문제로 보인다.

 

문제의 제약 조건을 활용하면 더욱 효과적이고 빠른 해결 방법을 찾을 수 있기 때문에, 문제를 풀기 전에 제약 조건을 먼저 확인하는 것이 중요하다는 이유를 잘 설명해 주는 모법적인(?) 문제라고 생각한다.

 

조금은 거친 방법으로 문제를 풀기

이 문제에서 주어진 두 개의 제약조건을 생각해보자. 첫 번째는 수열을 이루고 있는 숫자는 1부터 9까지라는 것이다. 두 번째는 최대 두 개의 숫자로만 이루어진 연속된 부분 수열의 최대 길이를 구해야 한다는 것이다. 이 두 가지 조건을 이용하면 우리는 조금은 거친 방법을 생각해 볼 수 있다.

 

만약에 우리가 연속된 부분 수열을 이루고 있는 숫자 두 개를 미리 정하고, 그 조건을 만족하는 연속된 부분 수열의 최대 길이를 구하는 문제를 푼다고 생각해 보자. 주어진 수열에서 숫자 \(P_1\) 또는 \(P_2\)로 이루어진 연속된 부분 수열들 중 최대 길이를 구하는 코드는 아래와 같이 작성할 수 있다.

int func(int p1, int p2) {
    for (int i = 0 ; i < n ; ++i) {
        P[i] = (A[i] == p1 || A[i] == p2);
    }
    
    int ret = 0, cnt = 0;
    
    for (int i = 0 ; i < n ; ++i) {
        if (P[i]) {
            cnt += 1;
        } else {
            cnt = 0;
        }
        
        ret = max(ret, cnt);
    }
    
    return ret;
}

그렇다면 우리가 생각해봐야 하는 \(P_1\), \(P_2\)의 쌍의 개수는 수열을 이루고 있는 숫자가 최대 9개이므로 \({}_9 \mathrm{ C }_2\)이다. 함수 func의 시간 복잡도가 \(\mathcal{O}(N)\)이기 때문에 모든 경우에 대해 func 함수를 계산해도 주어진 제한시간 내에 충분히 들어올 수 있다.

int ans = 0;
    
for (int i = 1 ; i <= 9 ; ++i) {
    for (int j = i + 1 ; j <= 9 ; ++j) {
        ans = max(ans, func(i, j));
    }
}

printf("%d\n", ans);

 

슬라이딩 윈도우 기법으로 문제 풀기

다음으로 이 문제를 효율적인 방법으로 풀 수 있는 슬라이딩 윈도우 기법을 이용한 풀이를 알아보자. 어떠한 연속된 구간 [lo, hi]가 있을 때 이 구간을 이루고 있는 숫자의 개수가 두 개보다 많다면, hi를 증가시켜도 그 구간을 이루고 있는 숫자의 개수는 두 개보다 같거나 작아질 수 없다. 하지만 lo를 증가시키면 구간의 길이가 작아지므로 그 구간을 이루고 있는 숫자의 개수는 적어도 지금보다 같거나 혹은 더 적어질 것이다. 이 아이디어를 이용하면 lo와 hi를 조정해 \(\mathcal{O}(N)\)의 시간복잡도를 이용해 문제를 해결할 수 있다.

int solve() {
    int lo = 0, ans = 0, dNums = 0;
    
    for (int hi = 0 ; hi < n ; ++hi) {
        ++F[A[hi]];
        
        if (F[A[hi]] == 1) {
            ++dNums;
        }
        
        while (dNums > 2) {
            --F[A[lo]];
            
            if (F[A[lo]] == 0) {
                --dNums;
            }
            
            ++lo;
        }
        
        ans = max(ans, hi - lo + 1);
    }
    
    return ans;
}

만약 수열을 이루고 있는 수의 종류가 많아지거나, 수열의 길이가 지금보다 더 커진다면 위에서 소개한 완전 탐색과 비슷한 방법으로는 문제를 풀기 어렵지만 슬라이딩 기법을 이용하면 해결할 수 있다.

'알고리즘 > 문제풀이' 카테고리의 다른 글

[백준 31963번] 두 배  (0) 2024.06.25
[백준 16937번] 두 스티커  (0) 2024.06.14
[백준 17845] 수강 과목  (0) 2024.06.11
[백준 2417번] 정수 제곱근  (0) 2024.05.30
[백준 15989번] 1, 2, 3, 더하기 4  (0) 2024.05.28

Designed by JB FACTORY