UVA 714 Copying Books(最大值最小化)

Copying Books
Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu

Description

Download as PDF


  Copying Books 

Before the invention of book-printing, it was very hard to make a copy of a book. All the contents had to be re-written by hand by so calledscribers. The scriber had been given a book and after several months he finished its copy. One of the most famous scribers lived in the 15th century and his name was Xaverius Endricus Remius Ontius Xendrianus (Xerox). Anyway, the work was very annoying and boring. And the only way to speed it up was to hire more scribers.


Once upon a time, there was a theater ensemble that wanted to play famous Antique Tragedies. The scripts of these plays were divided into many books and actors needed more copies of them, of course. So they hired many scribers to make copies of these books. Imagine you have m books (numbered $1, 2, \dots, m$) that may have different number of pages ( $p_1, p_2, \dots, p_m$) and you want to make one copy of each of them. Your task is to divide these books among k scribes, $k \le m$. Each book can be assigned to a single scriber only, and every scriber must get a continuous sequence of books. That means, there exists an increasing succession of numbers $0 = b_0 <b_1 < b_2, \dots < b_{k-1} \le b_k = m$ such that i-th scriber gets a sequence of books with numbers between bi-1+1 and bi. The time needed to make a copy of all the books is determined by the scriber who was assigned the most work. Therefore, our goal is to minimize the maximum number of pages assigned to a single scriber. Your task is to find the optimal assignment.

Input 

The input consists of N cases. The first line of the input contains only positive integer N. Then follow the cases. Each case consists of exactly two lines. At the first line, there are two integers m and k$1 \le k \le m \le 500$. At the second line, there are integers $p_1, p_2, \dots p_m$ separated by spaces. All these values are positive and less than 10000000.

Output 

For each case, print exactly one line. The line must contain the input succession $p_1, p_2, \dots p_m$ divided into exactly k parts such that the maximum sum of a single part should be as small as possible. Use the slash character (`/') to separate the parts. There must be exactly one space character between any two successive numbers and between the number and the slash.


If there is more than one solution, print the one that minimizes the work assigned to the first scriber, then to the second scriber etc. But each scriber must be assigned at least one book.

Sample Input 

2
9 3
100 200 300 400 500 600 700 800 900
5 4
100 100 100 100 100

Sample Output 

100 200 300 400 500 / 600 700 / 800 900
100 / 100 / 100 / 100 100
 
   
 
   
给你m本书,每本a[i]页(0<=i<m),把他们分成k个区间,使这k个区间和的最大值最小。。
我们可以先找到这m本书的最大页数max, 和所有书的页数和sum,则这k个区间中 每个区间的区间和一定在[mmax,sum]中,(即分为m个区间,我们要的结果就是这m个数中的最大值,如果分为一个区间,结果就是所有数的页数和)

我们可以用二分先找到这个值(最大值中最小的数),判定条件就是能否分成k份。

剩下的就是根据这个值标记分开的位置,但为了在有多种结果时,前面区间尽量小,应从后往前分开,如果要求k分,我们只用了k-1分就完成了,剩下的要在前面按顺序补上(详看代码吧)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <cmath>
#include <map>
#define MAX 0x3f3f3f3f
#define N 100010
using namespace std;
long long int a[N];
long long int mmax;
long long int sum;
int m,k;
bool judge(long long int n)  //以n为区间和中的最大值
{
    long long int s=0;        //记录区间和
    int sk=0;                 //记录区间分了几次
    for(int i=0; i<m; i++) 
    {
       if(s+a[i]>n)          //如果s+上a[i]超过了最大值n,说明a[i]前面要有一个/
       {
           sk++;            
           s=0;             //记录下一个区间的和
       }
       if(sk>=k)            //如果添加的’/‘超过了k-1,不满足要求,直接退出
        return false;
       s+=a[i];
    }
    return true;       
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&m,&k);
        mmax=-1;
        sum=0;
        for(int i=0; i<m; i++)
        {
            scanf("%lld",&a[i]);
            sum+=a[i];                       //所有页数的和
            mmax=max(mmax,a[i]);             //区间中的最大值
        }
        while(mmax<sum)                      //二分来找最大值中的最小值
        {
            long long int mid=(mmax+sum)/2;
            if(judge(mid))                   //判断以mid为最大值中的最小值是否能够K分
                sum=mid;                     //能的话在[mmax,mid]中是否有更小的
            else mmax=mid+1;                 //不能的话结果在[mid+1,sum]中
        }
        int rr=0;                           
        long long int nn=0;
        bool b[N];
        memset(b,false,sizeof(b));         //标记在哪输出'/',b[i]指在a[i]后面加一个'/'
        for(int i=m-1;i>=0;i--)            //倒着来,为了满足前面的能最小
        {
            if(nn+a[i]>mmax)               //超过最大值,在要在a[i+1]前面加一个'/'
            {
                b[i]=true;                
                nn=0;                      //重新计数
                rr++;                      //记录添加了几次'/'
            }
            else if(nn+a[i]==mmax)         //该区间正好等于最大值,在a[i]前面加'/'
            {
                b[i-1]=true;
                nn=0;
                rr++;                     //记录添加了几次'/'
                continue;
            }
            nn+=a[i];
        }
        int e=k-rr-1;               //如果e大于0,说明输出的'/'少于要求,要在前面没有标记的位置补上
        for(int i=0;i<m-1;i++)
        {
           printf("%lld ",a[i]);
           if(b[i])
            printf("/ ");
           else if(e)               //补少于k-1的'/'
           {
                printf("/ ");
                e--;
           }
        }
        printf("%lld\n",a[m-1]);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值