PS/백준

[백준] 22953 도도의 음식 준비 (C++)

dong_gas 2021. 9. 1. 16:05
 

22953번: 도도의 음식 준비

첫째 줄에 요리사의 수 $N$ ($1 \le N \le 10$), 만들어야 할 음식의 개수 $K$ ($1 \le K \le 1\,000\,000$), 격려해줄 수 있는 횟수 $C$ ($0 \le C \le 5$)가 주어진다. 둘째 줄에 길이가 $N$인 정수 수열 $A$가 주어

www.acmicpc.net

 이번 여름 '신촌 알고리즘 연합 캠프 콘테스트 - 초급'에 나온 문제이다. 그 당시에 나는 이 문제를 풀지 못했었다. 문제에서 주방장이 요리사에게 격려하는 과정이 Greedy할 것이라고 생각해서 풀었기 때문이다. 격려를 한 뒤, 이분 탐색을 통해 최소 시간을 찾는 방식으로 문제를 풀었었고 당연히 틀렸다.

 

 

 대회가 끝나고 다시 문제를 보니 격려해줄 수 있는 횟수가 매우 작아서 브루트포스 기법을 이용해 가능한 모든 경우의 수대로 격려를 하고, 이분 탐색을 통해 최소 시간을 찾을 수 있을 것 같더라. 아래의 풀이가 이 방식이다.

 

 

 지금 이 문제를 보니까, 이번 신촌 연합 알고리즘 캠프에서 배운 브루트포스와 이분탐색을 배운 그대로 써먹어서 풀 수 있게 잘 만든 문제인 것 같다. 아래의 코드도 브루트포스, 이분탐색 숙제할 때 작성했던 코드를 붙여놓은 느낌이다.

 


풀이

 solve함수를 통해 문제를 해결한다. solve함수의 input은 C값이다.

 

 solve함수에서는 C가 양수이면 요리사의 시간을 1 줄이고, solve(C-1)을 호출한다. 그 후에는 방금 줄인 요리사의 시간을 다시 원상태로 돌려놓는다. 이 과정이 코드의 39~52번 째 줄이다.

 

 solve함수에서 C가 0이되면(=격려를 모두 완료하면) 이분탐색을 통해 최소 요리시간을 찾는다. mid값(음식 조리시간)일 때, K개 이상 만들 수 있는지 확인하는 possible함수를 이용하였다. mid값일 때, K개 이상 만드는 것이 가능하다면 그 시간보다 더 오래걸리는 시간은 모두 가능하기 때문에, high값을 mid-1로 바꾸어준다. 반면, mid값일 때, K개 이상 만드는 것이 불가능 하다면, 그 답은 그 mid값보다 크기 때문에, low값을 mid+1로 바꾸어주면서 풀었다. 여튼 그렇게 구한 최소 조리시간이 ans(답)보다 작다면 그 값이 ans(답)이다. 이 과정을 가능한 모든 경우만큼 실행하므로 올바른 답을 구할 수 있다.

 

 possible 함수에서는 각 요리사가 주어진 시간에 몇 개를 만드는지 정수 단위로 계산하여 모두 더해주었다. 그 값이 K이상이면 true를 반환한다. 아, 그리고 아래의 코드 15번 째 줄에서

sum += ll(sec / vec[i]); 가 아닌
sum += ll(sec / max(ll(1), vec[i])); 를 이용하였다.

그 이유는 vec[i] (i번 째 요리사의 음식 조리시간)가 1이하로 내려갈 수 없기 때문이다! 나는 이 부분을 놓쳐서 몇 번 틀렸었다. 이 부분을 놓치면 아래와 같은 반례들이 있다.

 

반례 2가지

더보기

3 3 1

1 1 1

 

3 3 2

1 2 1


코드

#include <iostream>
#include <bits/stdc++.h>
#define endl '\n'
#define INF 98760000000000
using namespace std;
using ll = long long;

ll n, k, c, a;
ll ans = INF;
vector<ll> vec; //요리사의 음식 조리 시간

bool possible(ll sec) { //음식 조리시간이 sec일 때, K개 이상 만들 수 있는지 확인하는 함수
    ll sum = 0; //총 만드는 음식의 갯수
    for (ll i = 0; i < vec.size(); i++) {
        sum += ll(sec / max(ll(1), vec[i])); //각 요리사는 초당 (1/vec[i])개를 만들 수 있음
    }
    if (sum >= k) return true;
    else return false;
}

void Binary_Search() { //시간 이분탐색
    ll low = 1, high = INF;
    ll mid = (low + high) / 2;
    ll tmp = INF;
    while (low <= high) {
        mid = (low + high) / 2;
        if (possible(mid)) { //음식조리시간이 mid일 때, K개 이상 만들 수 있다면
            tmp = min(tmp, mid);
            high = mid - 1;
        }
        else { //음식조리시간이 mid일 때, K개 이상 만들 수 없다면
            low = mid + 1;
        }
    }

    if (tmp < ans) ans = tmp; //이분탐색을 통해 찾은 값이 ans보다 작으면 그 값이 ans가 된다.
}

void solve(ll chance) { //브루트포스이용
    if (chance == 0) { //c값이 0이면 값 계산
        Binary_Search();
        return;
    }
    else {
        for (ll i = 0; i < vec.size(); i++) {

            vec[i]--; //격려 1번
            solve(chance - 1); //격려 1번했으니까 chance값 1 빼주기
            vec[i]++; //격려하기 전으로 되돌려 놓는 작업
        }
    }
}

int main() {
    ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);

    cin >> n >> k >> c;

    for (ll i = 0; i < n; i++) {
        cin >> a;
        vec.push_back(a);
    }

    solve(c); //주어진 c값을 가지고 문제 해결

    cout << ans << endl; //답 출력

    return 0;
}

 

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

[백준] 23247 Ten (C++)  (0) 2021.10.13
[백준] 1736 쓰레기 치우기 (C++)  (6) 2021.10.11
[백준] 2206 벽 부수고 이동하기 (C++)  (0) 2021.09.10
[백준] 15650 N과 M(2) (C++)  (0) 2021.09.10
[백준] 14502 연구소 (C++)  (0) 2021.09.05