Description
Given n books and the ith book has A[i] pages. You are given k people to copy the n books.
n books list in a row and each person can claim a continous range of the n books. For example one copier can copy the books from ith to jth continously, but he can not copy the 1st book, 2nd book and 4th book (without 3rd book).
They start copying books at the same time and they all cost 1 minute to copy 1 page of a book. What's the best strategy to assign books so that the slowest copier can finish at earliest time?
中文描述:
给出一个数组A包含n个元素,表示n本书以及各自的页数。现在有个k个人复印书籍,每个人只能复印连续一段编号的书,比如A[1],A[2]由第一个人复印,但是不能A[1],A[3]由第一个人复印,求最少需要的时间复印所有书。
Example
Given array A = [3,2,4], k = 2.
Return 5( First person spends 5 minutes to copy book 1 and book 2 and second person spends 4 minutes to copy book 3. )
解题思路:
这道题和wood cut 一样 属于相同类型的题, wood cut需要找到最大值 而这里需要找到最小值。 因此我们仍然是用二分法来做。那么首先, 最小值应该是0, 因为有可能传入的数组是空数组,也就是没有书, 那么需要的复印时间最小就为0. 那么最大值呢?最大值理论上应该是数组里面的所有数据加起来之和。 也就是全部书都拿给一个人复印, 由于多人复印时间是并行的, 那么一个人把所有书都复印完肯定是最长的时间。 但是由于不确定输入数据 遍历一遍数组求和其实没有必要,所以最大值姑且赋一个Integer.MAX_VALUE就好。
代码:
public class Solution {
/**
* @param pages: an array of integers
* @param k: An integer
* @return: an integer
*/
public int copyBooks(int[] pages, int k) {
// write your code here
int min = 0, max = Integer.MAX_VALUE;
while(min + 1 < max)
{
int mid = min + (max - min) / 2;
if(check(pages, mid, k))
{
max = mid;
}else
min = mid;
}
if(check(pages, min, k))
return min;
return max;
}
public boolean check(int[] pages, int limit, int k)
{
int num = 0;
int left = 0;
for(int item : pages)
{
if(item > limit)
return false;
if(item > left)
{
num++;
left = limit;
}
left -= item;
}
return num <= k;
}
}
判断条件: 猜测的复印时间应该是一个人的复印时间限制, 如果数组中有一个数据的值大于这个时间限制。说明是不可能在这个时间内复印完的 直接返回false。num用来记录可能的需要复印的人数, 而left则表示每个人剩余的复印时间, 如果书的页数大于一个人所剩余的复印时间, 那么说明还需要多一个人来复印这本书, 然后num加1, left重置为limit之后再减去item,留给下一次接着判断。 最后 如果num小于等于k, 说明这个时间可行, 那么改变max, 反之改变min。 最后检查min是否满足条件(检查min是因为求的是最小值),若是满足条件直接返回,不满足就返回max。