LintCode 437: Copy Books (贪婪 + 二分 经典题!!!)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/roufoo/article/details/87579774
  1. 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。
从两个方向看:

  1. 假设T等于1个人抄完所有书所需时间,那所需最少抄写员就是1。如果1<k,说明T取太大了,要把T减小。
  2. 假设T是最厚的那本书由1个人抄完所需时间,那所需最少抄写员就是N。如果N>k,说明T取太小了,要把T变大。
    这个思路告诉我们可以用Binary Search来解决这个问题。1)和2)分别给出了T的上下限,如果给定T所需人数<k,则T要变小,如果所需人数>k, 则T要变大。若所需人数=k, 说明此T可以,但还可以试试能不能把T变小。
    checkValid()就是判断N本书,<=T时间, k个人能不能搞定。

注意:题目问“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;
    }
};

猜你喜欢

转载自blog.csdn.net/roufoo/article/details/87579774
今日推荐