알고리즘

숫자구슬 (boj 2613)

KyooDong 2020. 6. 5. 01:46
728x90

숫자구슬 문제는 Parametric search 기법을 활용해서 풀 수 있어요. 용돈관리 문제랑 상당히 유사하고 코드도 거의 똑같습니다만..

약간의 함정이 숨어있어요.

M개의 그룹으로 나누고 각 그룹에 속한 원소의 수를 출력해줘야하는데

 

입력으로

8 5
1000 1 1 1 1 1 1 1

 

같이 극단적으로 큰 값을 주게 되면, 용돈관리 코드로는

 

1000
1 7

과 같이 그룹이 5개가 아니라 2개만 나오게 돼요

그래서 충분히 적은 수의 그룹을 만들 수 있더라도 M개에 미치지 못한다면 적절히 쪼개주는 작업이 필요하답니다.

쪼개는건 사실 쉬워요. 원소의 갯수가 2 이상인 그룹을 찾아서 1개씩 뽑아주면 됩니다. 뽑아주는 과정은 그룹의 갯수가 M이 될때까지 반복하는거구요.

 

코드

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>

#define INF 987654321

int N, M;
int arr[310];
int max = 30000;
int min;
std::vector<int> result;


int parametricSearch(int k) {
    int val = k;
    int m = 1;

    for (int n = 0; n < N; n++) {
        if (val >= arr[n]) {
            val -= arr[n];
        } else {
            val = k;
            val -= arr[n];
            m++;
        }
    }

    return m <= M;
}

int main() {
    scanf("%d %d", &N, &M);

    for (int n = 0; n < N; n++) {
        scanf("%d", arr + n);

        min = std::max(min, arr[n]);
    }

    while (min != max) {
        int val = min + (max - min) / 2;
        if (parametricSearch(val)) {
            max = val;
        } else {
            min = val + 1;
        }
    }

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

    int value = max;
    int count = 0;
    int groupCount = 0;
    for (int n = 0; n < N; n++) {
        if (value >= arr[n]) {
            value -= arr[n];
            count++;
        } else {
            value = max;
            value -= arr[n];
            result.push_back(count);
            count = 1;
            groupCount++;
        }
    }
    result.push_back(count);

    int i = 0;
    while (result.size() < M) {
        while (result[i] <= 1)
            i++;

        result[i]--;
        result.push_back(1);
    }

    for (int r : result)
        printf("%d ", r);
    return 0;
}