티스토리 뷰

[Codeforces 1084B] Kvass and the Fair Nut


N개의 음료수의 용량(v_i)과 s가 주어진다. 각각의 음료수에서는 최소 0부터 최대 (v_i)까지 음료수를 빼낼 수 있다. 이렇게 음료는 빼내서 s가 되도록 만들어야 한다. 이때 가장 적게 든 음료수의 용량이 최대가 되도록 하는 문제이다.

처음 딱 봤을 때는 당연히 바이너리 서치로 푸는 문제 인 줄 알았다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
int fMax(ll a,ll b){
    return a>b?a:b;
}
int main(){
    ll n,s;    scanf("%lld %lld",&n,&s);
    ll arr[1009];
 
    ll sum=0;
    int l=0,r=0;
 
    for(int i=0 ; i<n ; i++){
        scanf("%lld",&arr[i]);
        sum+=arr[i];
        r=fMax(r,arr[i]);
    }
 
    if(sum<s){
        printf("-1\n");
        return 0;
    }
    ll ans;
 
    //upper bound
    while(r > l+1){
        int mid=(l+r)/2;
        ll cur_sum=0;
        bool check=false;
        
        for(int i=0 ; i<n ; i++){
            int diff=arr[i]-mid;
            if(diff<0){
                check=true;
                break;
            }
            cur_sum+=diff;
        }
 
        if(cur_sum<|| check){
            r=mid;
        }
        else{
            l=mid;
        }
    }
    printf("%d\n",l);
    return 0;
}
cs





그런데 사실 그리디로도 풀 수 있어서 O(N)만에 가능하다(바이너리 서치는 O(N*logN)). N=1000으로 작았기에 망정이지, 1000만 정도 됐으면 아마 못 풀었을 거다.

그리디 풀이에서의 핵심은 문제의 답은 항상 입력으로 주어진 v 중에서 가장 작은 v값보다 작거나 같다는 것이다. 음료수를 채우는 행위는 불가능하기 때문이다. 그래서 우선 모든 음료수의 용량에서 가장 작은 음료수의 용량(=min_cap이라고 하자)을 빼면서 동시에 그 차를 diff 변수에 저장한다. 이 시행을 하고나면 모든 음료수의 용량은 min_cap과 동일해진다. 그리고 diff의 값이 s보다 크다면 정답은 바로 min_cap이 된다. 만약 여전히 s가 더 크다면 남은 음료수의 양이라는 의미에 remain변수를 만들고 이 값은 (s-diff)와 같다. 그리고 remain을 n으로 나눈 몫을 min_cap에서 빼준다. 이 시행은 남아있는 음료수 양에 대해서 모든 음료수가 공평하게 나눠준다는 뜻이다. 그래야만 남아있는 최소 용량이 최대가 되도록 할 수 있다. 그리고 reamin에서 n으로 나누어 떨어지지 않는다면 추가로 1을 더 빼주면 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
int arr[1009];
int main(){
    ll n,s;    scanf("%lld %lld",&n,&s);
 
    ll sum=0;
    int min_cap=1<<30;
    for(int i=0 ; i<n ; i++){
        scanf("%d",&arr[i]);
        sum+=arr[i];
        min_cap=min(min_cap,arr[i]);
    }
 
    if(sum<s){
        printf("-1\n");
        return 0;
    }
 
    ll diff=0,ans=0;
    for(int i=0 ; i<n ; i++){
        diff+=(arr[i]-min_cap);
    }
    ans=min_cap;
    if(diff<s){
        ll remain=s-diff;
        ans-=(remain/n);
        if(remain%n) ans--;
    }
    printf("%lld\n",ans);
    return 0;
}
cs






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

[Codeforces 950C] Zebras  (0) 2019.05.21
[Codeforces 1036D] Vasya and Arrays  (0) 2019.04.24
[Codeforces 1066B] Heaters  (0) 2019.02.18
[Codeforces 1110B] Tape  (0) 2019.02.11
[Codeforces 1070D] Garbage Disposal  (0) 2019.02.11
댓글