714 - Copying Books

来源:互联网 发布:人工智能 招聘 深圳 编辑:程序博客网 时间:2024/06/09 14:05

Before theinvention of book-printing, it was very hard to make a copy of a book. All thecontents had to be re-written by hand by so called scribers. Thescriber 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 wasXaverius Endricus Remius Ontius Xendrianus (Xerox). Anyway, the work wasvery annoying and boring. And the only way to speed it up was to hire morescribers.


Once upon a time, there was a theater ensemble that wanted to play famousAntique Tragedies. The scripts of these plays were divided into many books andactors needed more copies of them, of course. So they hired many scribers tomake copies of these books. Imagine you have m books(numbered ) that may havedifferent number of pages ( ) and youwant to make one copy of each of them. Your task is to divide these booksamong k scribes, . Each book can beassigned to a single scriber only, and every scriber must get a continuoussequence of books. That means, there exists an increasing succession of numbers suchthat i-th scriber gets a sequence of books with numbersbetween bi-1+1 and bi.The time needed to make a copy of all the books is determined by the scriberwho was assigned the most work. Therefore, our goal is to minimize the maximumnumber of pages assigned to a single scriber. Your task is to find the optimalassignment.

Input 

The input consistsof N cases. The first line of the input contains only positiveinteger N. Then follow the cases. Each case consists of exactly twolines. At the first line, there are two integers m and k, . At thesecond line, there are integers separatedby 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 dividedinto exactly k parts such that the maximum sum of a singlepart should be as small as possible. Use the slash character (`/') to separatethe parts. There must be exactly one space character between any two successivenumbers and between the number and the slash.


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

Sample Input 

2

9 3

100 200 300 400500 600 700 800 900

5 4

100 100 100 100100

Sample Output 

100 200 300 400500 / 600 700 / 800 900

100 / 100 / 100 / 100100

题意:

按顺序给你N个数,将这N个数分成连续的M段,使得这M段每段的和中的最大值最小,输出最小值。如果有多种可能的话,尽量在前面进行划分。

 

思路:最小化最大值的问题

题目的最小的最大值一定大于等于单个元素的最大值(maxp),小于等于所有元素的和(tot)。在[maxp, tot]内寻找到分组不多于K组的最小数字(不多于K组:解释具体见下面的第二组数据分析)。为了与输出相统一,从后面开始统计分组,从而使得后面的区间和尽量大。使用二分搜索区间[maxp, tot]的数字,具体细节见代码。求出ans后,用贪心的思想输出序列,主要是控制‘/’的输出。具体细节见代码。

 

代码:

#include<cstdio>

#include<cstring>

//#include<iostream>

#include<algorithm>

using namespacestd;

 

const int maxm =500 + 5;

int m, k, p[maxm];

int last[maxm];

/*

每一组都要清空该数组为0;last[i]=1表示p[i]是分组区间的最后一个数,即输出p[i]后要输出斜杠

*/

 

int solve(longlong maxp) //从后向前统计组数

{

    long long done = 0;

    int ans = 1;//至少一组

    for(int i = m-1; i >=0; i--)

    {

        if(done + p[i] <= maxp)

        {

            done += p[i];

        }

        else

        {

            ans++;

            done = p[i];

        }

    }

    return ans;

}

 

void print(longlong ans)

{

    long long done = 0;

    memset(last, 0, sizeof(last));

    int remain = k-1;//剩余的斜杠数目

    for(int i = m-1; i >= 0; i--)

    {

        if(done + p[i] > ans)

        {

            last[i] = 1;

            remain--;

            done = p[i];

        }

        else

        {

            done += p[i];

        }

        if(remain==i+1)

/*

当理论分组数目<K时,当剩余斜杠数目==剩余的数字个数,则每个数字后面都要输出斜杠,强制分组

*/

        {

            for(int j=0;j<=i;j++)

            {

                last[j]=1;

            }

        }

    }

    for(int i = 0; i < m-1; i++)

    {

        printf("%d ", p[i]);

        if(last[i])

        {

            printf("/ ");

        }

    }

    printf("%d\n", p[m-1]);

}

 

int main()

{

    int T;

    scanf("%d", &T);

    while(T--)

    {

        scanf("%d%d", &m,&k);

        long long tot = 0;

        int maxp = -1;

        for(int i = 0; i < m; i++)

        {

            scanf("%d", &p[i]);

            tot += p[i];

            maxp = max(maxp, p[i]);

        }

        long long L = maxp, R = tot;//使用 long long 否则有可能爆掉

        while(L < R)

        {

            long long M = L + (R-L)/2;

            //cout<<"当前的中点值是:"<<M<<endl;

            //cout<<"分组的数目是:"<<solve(M)<<endl;

            if(solve(M) < k)

//分组数目少,说明最大值太大,减小上限,不用保留上限

            {

                R=M-1;

            }

            else if(solve(M) == k)

//分组数目正好,但是不能说明最大值是最小的,减小上限,同时保留上限

            {

                R = M;

            }

            else //分组数目多,说明最大值太小,增大下限,不用保留下限

            {

                L = M+1;

            }

        }

        print(L);

    }

    return 0;

}

 

“在[maxp, tot]内寻找到分组不多于K组的最小数字”分析如下:

以第二组数据为例,运行注释掉的代码:

 

示例输入:

1

5 4

100 100 100 100100

 

对应输出:

当前的中点值是:300

分组的数目是:2

当前的中点值是:199

分组的数目是:5

当前的中点值是:249

分组的数目是:3

当前的中点值是:224

分组的数目是:3

当前的中点值是:211

分组的数目是:3

当前的中点值是:205

分组的数目是:3

当前的中点值是:202

分组的数目是:3

当前的中点值是:200

分组的数目是:3

100 / 100 / 100 /100 100

分析:此组数据始终没有找到适合的M值使得solve(M) == k,当L==R时,退出了二分搜索循环,此时分组数目为3.

如果输出的时候不强制分组,输出应该是:

100 / 100 100 /100 100

但是根据题目要求,必须要输出K-1个斜杠,所以必须要考虑强制分组。

0 0
原创粉丝点击