抄书问题之二

Given n books( the page number of each book is the same) and an array of integer with size k means k people to copy the book and the i th integer is the time i th person to copy one book). You must distribute the continuous id books to one people to copy. (You can give book A[1],A[2] to one people, but you cannot give book A[1], A[3] to one people, because book A[1] and A[3] is not continuous.) Return the number of smallest minutes need to copy all the books.

样例

Given n = 4, array A = [3,2,4], .

Return 4( First person spends 3 minutes to copy book 1, Second person spends 4 minutes to copy book 2 and 3, Third person spends 4 minutes to copy book 4. )


解题思路:

1.二分法,最优解。

2 动态规划,时间复杂度很高O(N2 × K)。

class Solution {
public:
    /**
     * @param n: an integer
     * @param times: a vector of integers
     * @return: an integer
     */
    // V1 二分法 (最优解)
    int copyBooksII(int n, vector ×) {
        if(n == 0){
            return 0;
        }
        int k = times.size();
        if(k == 0){
            return numeric_limits::max();
        }
        else if (k == 1){
            //只有一个抄书员
            return times[0] * n;
        }
        //找出抄书时间的上下限
        // 所有人以最快速度抄书,得到下限
        // 所有人以最慢速度抄书,得到上限
        int lowBound = numeric_limits::max();
        int highBound = numeric_limits::min();
        for(int i = 0; i < k; i++){
            lowBound = min(lowBound, times[i]);
            highBound = max(highBound, times[i]);
        }
        lowBound =  lowBound * (( n + k - 1) / k);
        highBound = highBound * (( n + k - 1) / k);
        
        // 这里需要排序,将速度最快的排在前面,这样便于canCopy判断
        sort(times.begin(), times.end());
        
        // 二分法找出答案,每次二分时判断间值是否能在target时间内抄完
        while(lowBound < highBound){
            int mid = lowBound + (highBound - lowBound)/2;
            if(canCopy(mid, n, times)){
                highBound = mid;
            }
            else{
                lowBound = mid + 1;
            }
        }
        return lowBound;
    }
    // 判断是否能在target时间内抄写完成
    // 优先选择最快的人抄写,因此事前需要对times 排序。
    // 抄写速度最快的人尽量多抄
    bool canCopy(int target, int n, vector ×){
        int totalBookCount = n;
        int sum = 0;
        int i = 0;
        while(i < times.size()){
            if(sum + times[i] <= target){
                sum += times[i];
            }
            else if (i != (times.size() - 1) && 
                     times[++i] <= target){
                sum = times[i];
            }
            else {
                return false;
            }
            
            totalBookCount--;
            
            if(totalBookCount <= 0){
                return true;
            }
        }
        return false;
    }
    //////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////
    // V2 : DP, time limit exceeded O(N2 * K)
    int copyBooksII1(int n, vector ×) {
        // write your code here
        if(n == 0){
            return 0;
        }
        int k = times.size();
        if(k == 0){
            return numeric_limits::max();
        }
        else if (k == 1){
            //只有一个抄书员
            return times[0] * n;
        }
        //f[i][j] 表示i个人抄j本书的最小耗时
        vector > f(k+1, vector(n+1, 0));
        for(int j = 1; j <= n; j++){
            f[0][j] = numeric_limits::max();
            f[1][j] = times[0] * j;
        }
        for(int i = 1; i <= k; i++){
            for(int j = 1; j <= n; j++){
                int minTime = numeric_limits::max();
                for(int x = 0; x <= j; x++){
                    minTime = min(minTime,
                                  max(f[i-1][x], (j - x) * times[i-1]));
                }
                f[i][j] = minTime;
            }
        }
        return f[k][n];
    }
    //V3 : DP 优化, 滚动数组空间优化,但是时间复杂度仍然比较高
    int copyBooksII2(int n, vector ×) {
        // write your code here
        if(n == 0){
            return 0;
        }
        int k = times.size();
        if(k == 0){
            return numeric_limits::max();
        }
        else if (k == 1){
            //只有一个抄书员
            return times[0] * n;
        }
        //f[i][j] 表示i个人抄j本书的最小耗时
        vector > f(2, vector(n+1, 0));
        for(int j = 1; j <= n; j++){
            f[0][j] = numeric_limits::max();
        }
        for(int i = 1; i <= k; i++){
            for(int j = 1; j <= n; j++){
                int minTime = numeric_limits::max();
                for(int x = j; x >= 0; x--){
                    minTime = min(minTime,
                                  max(f[(i-1)%2][x], (j - x) * times[i-1]));
                    
                    if(f[(i-1)%2][x] < (j - x) * times[i-1]){
                        break;
                    }
                    
                }
                f[i%2][j] = minTime;
            }
        }
        return f[k%2][n];
    }
    
};




你可能感兴趣的:(算法)