- Copy Books
中文English
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?
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. )
解法1:
这道题最快的解法是:Greedy + Binary Search
首先我们思考这样一个问题: 如果给定N本书,我们限定时间不超过T,所需最少的抄写员是多少?
这个问题可以用贪婪法。因为题目规定了,**同一本书只能由一个人编辑,并且只是连续编辑多本书。**所以,我们让第一个人从第一本书开始抄,一直抄到时间超过T的时候某本书(假设为第x本书)还没抄完,则第1个人就只能抄到第x-1本书,第x本书则由第2个人负责抄,一直到他抄到某本书又超时了为止。如此循环下去,直到所有书都抄完为止,所需要人数即为所需最少抄写员。
现在我们就可以试不同的T,看所需最少抄写员是不是超过了k。
从两个方向看:
- 假设T等于1个人抄完所有书所需时间,那所需最少抄写员就是1。如果1<k,说明T取太大了,要把T减小。
- 假设T是最厚的那本书由1个人抄完所需时间,那所需最少抄写员就是N。如果N>k,说明T取太小了,要把T变大。
这个思路告诉我们可以用Binary Search来解决这个问题。1)和2)分别给出了T的上下限,如果给定T所需人数<k,则T要变小,如果所需人数>k, 则T要变大。若所需人数=k, 说明此T可以,但还可以试试能不能把T变小。
checkValid()就是判断N本书,<=T时间, k个人能不能搞定。
上面好像没说对。实际上最短要花费的时间min就是最厚的那本书由一个人抄所花费的时间(即使有k个人,每本书也只能由一个人抄)。最长要花费的时间max就是由一个人抄所有的书所花费的时间。我们在min和max之间二分查找,如果mid时间可以由k个人抄完(<=k),说明mid可能还可以取小点,否则mid就要取大点。
注意:题目问“What’s the best strategy to assign books so that the slowest copier can finish at earliest time?” 这是一个典型的最大值最小化问题。通常这类问题要先考虑用Binary Search。
代码如下:
class Solution {
public:
/**
* @param pages: an array of integers
* @param k: An integer
* @return: an integer
*/
int copyBooks(vector<int> &pages, int k) {
int len = pages.size();
int maxPage = 0, sum = 0;
for (int i = 0; i < len; ++i) {
maxPage = max(maxPage, pages[i]);
sum += pages[i];
}
int left = maxPage, right = sum;
while (left + 1 < right) {
int mid = left + (right - left) / 2;
if (checkValid(pages, mid, k)) {
right = mid;
} else {
left = mid;
}
}
if (checkValid(pages, left, k)) return left;
return right;
}
bool checkValid(vector<int> &pages, int limit, int k) {
int count = 1;
int sum = 0;
int len = pages.size();
for (int i = 0; i < len; ++i) {
if (pages[i] > limit) return false;
if (pages[i] + sum > limit) {
count++;
sum = pages[i];
} else {
sum += pages[i];
}
}
return count <= k;
}
};