714 - Copying Books(二分)

该题是所谓“最大值尽量小”的典型代表,方法就是用二分猜这个最值,判断函数就是从前向后扫,尽量向后划,如果最后划出的组数比k小,那么显然可以划成k组。

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T,n,k,a[505];
bool P(int m) {
    int ans=0,cnt=1;
    for(int i=0;i<n;i++) {
        ans+=a[i];
        if(ans>m) {
            ans = a[i]; cnt++;
        }
    }
    if(cnt<=k) return true;
    else return false;
}
int main() {
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&k);
        ll l = 0,r = 0;
        for(int i=0;i<n;i++) {
            scanf("%d",&a[i]);
            r += a[i];
            if(l<a[i]) l = a[i];
        }
        ll m;
        while(r > l) {  //二分查找最大和的最小可能
            m = (l+r)/2;
            if(P(m)) r = m;
            else l = m+1;
        }
        int ans[505];
        memset(ans,0,sizeof(ans)); //用来记录每一部分的数字个数
        int cnt = 0;
        ll bbc = 0;
        for(int i=n-1;i>=0;i--){
            bbc += a[i];
            if(bbc>r){
                bbc = a[i]; cnt++;//但是这样做是有缺陷的,比如本来应该是 1 1 2的可能变成 0 2 2
                ans[cnt]++;
            }
            else ans[cnt]++;
        } cnt ++;
        int q = 0;
        for(int i=k-1;i>=0;i--){ //因此将为0的部分用后面的数补上
            if(ans[i]!=0) break;
            else {
                for(int j=i-1;j>=0;j--){
                    if(ans[j]>1){
                        ans[j]--; ans[i]++; break;
                    }
                }
            }
        } 
        for(int i=k-1;i>=0;i--){    //打印
            for(int j=0;j<ans[i];j++){
                if(q==n-1) cout<<a[q++];
                else cout<<a[q++]<<' ';
            }
            if(q!=n)
            cout<<"/ ";
        }
        cout<<"\n";
    }
    return 0;
}


你可能感兴趣的:(二分查找,ACM,uva)