1 minute read

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

이진탐색(binary search)을 이용하는 문제이다.

푸는 방법:

‘left = 1(보석 1개)’, ‘right = 가장 많은 보석수’로 설정한다.

‘mid = (left + right) / 2’인데, mid가 한 사람이 받은 가장 많은 수의 보석(=질투심)이다.

이제 질투심 = mid 일때, 보석을 나눠줄 수 있는 사람의 최소값을 계산해야한다.

최소 인원수 계산 방법은 아래와 같다.

‘cnt += (보석수) / mid’하고, 만약 보석의 수가 mid로 나누어 떨어지지 않으면 ‘cnt++’를 해준다.

cnt++은 보석에서 mid의 배수만큼 빼고 남은 보석들을 한 명이 다 가져가는 것을 의미한다.

위의 과정을 ‘can_divide’라는 함수를 만들어서 처리해줬고, 만약 cnt <= N이면 true를, 아니면 false를 반환해줬다.

cnt가 나눠줄 수 있는 사람의 최소수인데, N이 이거보다 작으면 해당 mid값일 때 나눠줄 수 없다.

can_divide의 반환값이 true이면 현재 mid값을 저장해놓고 right = mid-1로 바꾸어 이진탐색을 계속한다.

질투심을 현재보다 더 내려보려는 시도이다.

반환값이 false라면 질투심을 현재보다 올려야한다. 따라서 left = mid+1로 바꾸어 이진탐색을 계속한다.

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;

vector<ll> j;

bool can_divide(ll N, int mid) {
    ll cnt = 0;
    for(int i=0; i<j.size(); ++i) {
        cnt += j[i] / mid;
        if(j[i] % mid != 0) cnt++;
    }

    if(cnt <= N) return true;
    else return false;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
  
    ll N;
    int M;
    cin >> N >> M;

    for(int i=0; i<M; ++i) {
        ll tmp;
        cin >> tmp;
        j.push_back(tmp);
    }

    ll right = *max_element(j.begin(), j.end());
    ll left = 1;

    ll ans;
    while(left <= right) {
        ll mid = (right + left) / 2;

        if(can_divide(N, mid)) {
            right = mid - 1;
            ans = mid;
        } else {
            left = mid + 1;
        }
    }

    cout << ans << '\n';
    return 0;
}

Leave a comment