티스토리 뷰

Computer Science/BOJ

BOJ 3487. Copying Books

yeongminb 2024. 11. 20. 15:27

문제 링크

 

문제 풀이

  이 문제는 $m\leq 500$ 개의 $10^8$ 이하의 숫자와 $k\leq m$ 이 주어질 때 $m$개의 숫자를 $k$개의 집합을 나누어 그 집합들의 합의 최대가 최소가 되도록 하는 문제이다. 이때 같은 집합에 포함되려면 그 수들이 모두 연속해야하며 원소에 개수가 1개 이상이어야한다. 예를 들어 9개의 수 100, 200, 300, 400, 500, 600, 700, 800, 900을 3개의 집합으로 나눈다고 할 때 100,200,300,400,500 / 600,700 / 800,900 으로 나눠야 최대합이 1700으로 최소가 된다. 참고로 나누는 방법이 여러개인 경우는 앞쪽의 집합의 원소의 개수가 가장 작을 때를 출력해야한다.

 

 일단 $m,k$의 범위가 작아서 백트랙킹을 해야하는 줄 알았다. 그런데 가능한 가짓수가 $P(n,k)$ 만큼 이므로 생각을 금방 접었다. 이후에는 이분탐색을 떠올렸는데, 최대합을 기준으로 이분탐색을 하면 최대합의 최소를 찾을 수 있을 것이라고 생각하였다. 또한 숫자의 최댓값을 $N$이라 할때 최대 합의 최소를 구하는 시간복잡도는 $O(logN)$ 밖에 안되므로 아주 넉넉하다. 그래서 매개변수 이분탐색의 방법을 사용하였다.  초기의 $l$은 0, $r$은 모든 숫자들의 합으로 잡아 계산하였다. 이분탐색에서 반환해야하는 값을 헷갈려서 조금 헤맸는데 $l \leq r$ 로 둔 다음에 모든 상황에서의 $mid$를 저장하고  조건을 만족시킬 때 $r = mid - 1$, 만족하지 않을 때는 $l = mid + 1$을 하여 최종적으로는 마지막 $mid$의 값을 출력해주면 됐다.

 

 이런식으로 최대합을 구하면 뒤쪽에서부터 그 합을 넘지않을 때까지 집합으로 분할해주면 되기 때문에 어렵지 않게 구현하였다. 

 

 

코드

#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;
typedef long long ll;
const int mmax = 500;

ll m,k;
ll books[mmax+1], sum=0;

void input(){
    cin>>m>>k;
    for(int i=0;i<m;i++){
        cin>>books[i];
        sum += books[i];
    }
}

ll BinarySearch() {
    ll l = 0;
    ll r = sum;      
    ll answer = r;  
    
    while (l <= r) {
        ll mid = (l + r) / 2;
        bool flag = true; 
        
        ll s = books[0], num = 1; 
        for (int i = 1; i < m; i++) {
            if(s > mid || books[i] > mid){
                flag =false;
                break;
            }
            
            if (s + books[i] > mid) {
                num++;
                s = books[i];
                if (num > k) {
                    flag = false;
                    break;
                }
            } else {
                s += books[i];
            }
        }
        
        if (flag) {
            answer = mid;
            r = mid - 1; 
        } else {
            l = mid + 1; 
        }
    }
    
    return answer;
}

void solve(){
    ll mxSum = BinarySearch();
    
    stack<int> st;
    ll s = books[m-1] , num = 1;
    st.push(m-1);
    for(int i=m-2;i>=0;i--){
        if(s + books[i] > mxSum || (i + 1 == k - num)){
            num ++;
            s = books[i];
            st.push(-1);
            st.push(i);
        } else {
            s += books[i];
            st.push(i);
        }
    }
    
    while(!st.empty()){
        int x = st.top();
        st.pop();
        if(x == -1) cout<<"/"<<" ";
        else {
            cout<<books[x]<<" ";
        }
    }
    cout<<"\n";
}

int main(){
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    
    int T; cin>>T;
    while(T--){
        sum = 0;
        input();
        solve();
    }
}

'Computer Science > BOJ' 카테고리의 다른 글

BOJ 3851. Jumping monkey  (0) 2024.12.31
BOJ 1353. 합과 곱  (1) 2024.12.07
BOJ 9202. Boggle  (2) 2024.11.20
BOJ 13141. Ignition  (3) 2024.10.30
BOJ 32074. XOR 최대  (1) 2024.10.26
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/09   »
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
글 보관함