문제 링크: https://www.acmicpc.net/problem/13164

 

13164번: 행복 유치원

행복 유치원 원장인 태양이는 어느 날 N명의 원생들을 키 순서대로 일렬로 줄 세우고, 총 K개의 조로 나누려고 한다. 각 조에는 원생이 적어도 한 명 있어야 하며, 같은 조에 속한 원생들은 서로

www.acmicpc.net


문제를 요약하면 다음과 같습니다.

  • 주어진 오름차순으로 정렬된 배열을 \(A\)라 할 때, \(A\)를 \(K\)개의 조로 분할해야 합니다. 이때 \(K\)개의 조의 조원들은 서로 인접해 있어야 합니다.
  • \(K\)개의 조로 나눌 때 아무렇게나 나누면 안되고, 각 조들의 비용 총합이 최소가 되도록 분할해야 합니다. 조의 비용은 해당 조의 (최댓값 - 최솟값)으로 계산합니다.

 

\(A\)가 정렬되어 있고, 각 조의 조원들이 서로 인접해 있으므로 각 조의 비용은 조원이 \(i, i + 1, i + 2, ... , j - 1, j\)일때 \(A_j - A_i\)가 됩니다.


이제 \(K\)개 조로 분할하는 방법을 생각해 봅시다. \(N\)명의 학생들을 \(K\)개 조로 분할해서 비용 총합이 최소인 것을 찾으려면 어떻게 해야 할까요?

가장 생각하기 쉬운 방법은 \(K\)개 조로 나누는 모든 방법을 시도하고, 이 중에 비용이 최소인 것을 고르는 것입니다. 이는 백트래킹을 사용하여 구할 수 있습니다.

하지만 이 방법은 \(N\)과 \(K\)의 제한이 \(300,000\)으로 높기 때문에 제한시간 내에 실행할 수 없습니다. 더 빠른 방법을 찾아야 합니다.

 

발상을 조금 바꿔봅시다. \(N\)명을 \(N\)개 조로 나눈 상황에서 시작해 봅시다. 이때 비용 총합은 당연하게도 \(0\)입니다.

여기서 \(N-1\)개 조로 조를 줄이려면, 기존에 있는 인접한 조 2개를 합쳐서 새로운 조를 하나 만들어야 합니다. 어떤 조 2개를 골라야 할까요?

원생 \(i\)의 키를 \(A_i\)라 하고, 원생 \(i, i+1, ..., j-1, j\)가 있는 조를 다음과 같이 표기하겠습니다.

\[[A_i, A_j]\]

이러면 \(N\)개 조를 다음과 같이 표현할 수 있습니다.

\[[A_1, A_1], [A_2, A_2], [A_3, A_3], ..., [A_N, A_N]\]

만약 조 \([A_1, A_1]\)과 \([A_2, A_2]\)를 합쳐 새로운 조 \([A_1, A_2]\)를 만들면 조의 개수는 \(N - 1\)개가 되고, 비용의 총합은 \(A_2-A_1\)이 됩니다.

그러면 \(A_{i+1}-A_i\)중에 가장 작은 것을 고른다면 조의 개수가 \(N - 1\)개 일때 비용 총합이 최소가 될 것입니다!

 

비슷한 방법으로 생각해 봅시다. 조가 \(T\)개 있다고 하고, \(T\)개 조가 다음과 같다고 합시다.

\[[A_1, A_{x_1}], [A_{x_1 + 1}, A_{x_2}], ..., [A_{x_{T-2}+1}, A_{x_{T-1}}], [A_{x_{T-1}+1}, A_N]\]

이 중 임의의 인접한 조 2개를 다음과 같이 골랐다고 해봅시다. 편의상 \(p, q, r\)의 표현을 사용합니다.

\[[A_p, A_q], [A_{q+1}, A_r]\]

이 두 조를 합쳐 새로운 조 \([A_p, A_r]\)를 만들게 되면 조의 개수는 \(T-1\)개가 되고, 비용의 총합이 변하게 됩니다.

기존 비용의 총합을 \(S_{old}\), 새로운 비용의 총합을 \(S_{new}\)라 하면 다음이 성립합니다.

\[S_{new}= S_{old} + (A_r - A_p) - (A_q - A_p) - (A_r - A_{q + 1}) = S_{old} + A_{q + 1} - A_q\]

즉 두 조를 합치면 두 조가 인접하는 부분에 있는 두 원소의 차이만큼 비용의 총합이 증가하게 됩니다! 

 

\(A\)는 오름차순 배열이므로 모든 \(q\)에 대하여 \(A_{q + 1} - A_q >= 0\)이 성립합니다. 따라서 비용 총합은 조가 합쳐져도 절대로 감소하지 않습니다.

따라서, 인접한 원소 차이가 가장 작은 것들을 계속 골라서 그 차이를 비용 총합에 더해주면 됩니다.

 

여기서 생각해 봐야할 것이 있습니다. 이전에 \(A_{x+1}\)와 \(A_x\)의 차이를 더해줬다면 또 이 값을 더할 수 있을까요?

\(A_x\)와 \(A_{x+1}\)이 더해졌다는 것은 이 두 값이 합쳐진 조의 양 끝이 아닌 중간에 들어갔다는 것을 의미합니다. 따라서 이 값들은 다시 사용할 수 없습니다.

즉, 어떤 \(x\)에 대하여 \(A_{x+1} - A_x\)는 비용 총합에 단 한 번만 더할 수 있습니다.

 

이제 문제를 해결할 수 있습니다. 인접한 두 원소의 차이 \(A_2 - A_1, A_3 - A_2, ..., A_N - A_{N-1}\)를 구하고, 오름차순으로 정렬합니다.

조를 한번 합칠 때 마다 조의 개수가 1개 줄어들고, 조가 \(N\)개인 상태로 시작하므로 총 \(N - K\)번 조를 합쳐주면 \(K\)개 조를 얻을 수 있습니다.

따라서 방금 정렬한 값들 중 앞의 \(N - K\)개를 더해주면, 비용 총합의 최솟값을 구할 수 있습니다.

시간 복잡도는 정렬과 덧셈을 합해 총 \(O(N \log N + N - K) = O(N \log N)\)입니다.

 

코드 (C++)

더보기
#include <bits/stdc++.h>

#define fastio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

using namespace std;
typedef long long ll;

int main() {
    fastio;
    
    int n, k;
    cin >> n >> k;
    
    ll cost = 0;
    vector<ll> height(n);
    
    for (int i = 0; i < n; i++) {
        cin >> height[i];
    }
    
    vector<ll> diff(n - 1);
    
    for (int i = 0; i < n - 1; i++) {
        diff[i] = height[i + 1] - height[i];
    }
    
    sort(diff.begin(), diff.end());
    
    for (int i = 0; i < n - k; i++) {
        cost += diff[i];
    }
    cout << cost;
    return 0;
}
// BOJ 13164

 

 

'Problem Solving > 백준' 카테고리의 다른 글

백준 27925 - 인덕션  (0) 2023.09.26
백준 13021 - 공 색칠하기  (0) 2023.09.24
백준 2482 - 색상환  (0) 2023.09.24
백준 7983 - 내일 할거야  (0) 2023.09.23
백준 23830 - 제기차기  (0) 2023.09.22

+ Recent posts