Uva714 Copying Books 【二分】【例题8-10】

题目:Copying Books

题意:将m个序列分成k个连续的子序列,将各个子序列求和,分的子序列的和的最大值尽量小。

思路:

(1)枚举子序列和的最大值,从m个序列中最大值~m个序列的和 之间的数二分枚举最大值,每次枚举的值看能划分的个数与k值比较,如果<=k 枚举值小缩小,否则增大,直到二分结束后,左值即为最大值的尽量小化。如果直接找到一次相等k的话不能不是尽量小化,所以继续进行二分!

(2)求划分个数:遍历一遍序列,连续累加数字,当加上下一位时大于当前枚举的最大值时就要划分了!

(3)打印划分:用一个标记数组,倒的遍历序列(因为要靠前的尽量小,所以先算后面的尽量达到最大值,剩下前面的数肯定少了就小了!)继续连续累加数字,当加上下一位大于最大值的话就将此位置标记1即为划分,并且将划分次数减少一次,  或者 当划分次数大于等于剩余数个数时必须划分了,因为有可能连续和还没有加到最大值,还一直加,此时剩余的个数不够满足k次划分了!

(4)输出原序列且当标记数组为1时输出"/"。

参考:紫书代码库

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int maxn = 500 + 5;
int s[maxn],mark[maxn],n,k;
inline int P(LL v){//最大值最小化为v时划分的次数
    int cnt = 1;
    LL sum = 0;
    for(int i=0;i<n;i++){
        if(sum + s[i] < v) sum += s[i];//向右划分
        else{sum = s[i];cnt++;}//记录划分次数
    }
    return cnt;
}
inline int binarySearch(LL l,LL r){//二分查找最大值
    while(l < r){
        LL pos = l + (r - l)/2;
        if(P(pos) <= k) r = pos;//当划分次数小于k时说明当前最大值大了,右值缩小
        else l = pos + 1;//
    }
    return l;
}
inline void divide(int ans){//划分
    memset(mark,0,sizeof(mark));
    LL sum = 0;
    int cnt = k;
    for(int i=n-1;i>=0;i--)
        if(sum + s[i] > ans || i+1 < cnt){//当和大于最大值 或  剩余的元素个数已经等于当前剩余的划分个数时必须划分,因为要求划分k次!
            sum = s[i];//将求和归位,继续下一次的相加
            mark[i] = 1;//在当前位置划分
            cnt--;//划分次数减少
        }
        else sum += s[i];

    for(int i=0;i<n-1;i++){
        printf("%d ",s[i]);
        if(mark[i]) printf("/ ");
    }
    printf("%d\n",s[n-1]);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&k);
        int maxp = -1;
        LL sum = 0;
        for(int i=0;i<n;i++) {
            scanf("%d",&s[i]);
            maxp = max(maxp,s[i]);//求最大值,用作二分查找最大值的左值
            sum += s[i];//求和,用作二分查找最大值的右值
        }
        divide(binarySearch((LL)maxp,sum));
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值