[LintCode]437 · 书籍复印

题目


437 · 书籍复印
算法
中等
通过率
40%
题目
题解
笔记
讨论
排名
描述
给定n本书,第i本书有pages[i]页。有k个人来抄这些书。

这些书排成一行,每个人都可以索取连续一段的书。例如,一个抄书人可以连续地将书从第i册复制到第j册,但是他不能复制第1册、第2册和第4册(没有第3册)。

他们在同一时间开始抄书,每抄一页书都要花1分钟。为了让最慢的抄书人能在最早的时间完成书的分配,最好的策略是什么?

请返回最慢抄书人花费的最短时间。

背完这套刷题模板,真的不一样!

北大学霸令狐冲15年刷题经验总结的《算法小抄模板Cheat Sheet》助你上岸!

微信添加【jiuzhang0607】备注【小抄】领取


书籍页数总和小于等于2147483647

样例
样例 1:

输入: pages = [3, 2, 4], k = 2
输出: 5
解释: 第一个人复印前两本书, 耗时 5 分钟. 第二个人复印第三本书, 耗时 4 分钟.
样例 2:

输入: pages = [3, 2, 4], k = 3
输出: 4
解释: 三个人各复印一本书.
挑战
时间复杂度 O(nk)

方法1:动态规划

        public int copyBooks(int[] pages, int k) {
            int n = pages.length;
            //f[t][i] 前t个人抄前i本书,最少需要的时间
            //转移方程:f[t][i] =min{max{f[k-1][j],pages[j]+...pages[i-1]}}(0<=j<=i)
            int[][] f = new int[k + 1][n + 1];
            int INF = Integer.MAX_VALUE;
            Arrays.fill(f[0], INF);
            f[0][0] = 0;
            //边界条件:
            //1.0个人抄0本书 f[0][0] = 0;
            //2.0个人抄1...n本书,f[0][1]=f[0][2]=...=f[0][n]=INF
            //3.t个人抄0本书 f[t][0] = 0;
            for (int t = 1; t <= k; t++) {
                f[t][0] = 0;
                for (int i = 1; i <= n; i++) {
                    f[t][i] = INF;
                    int sum = 0;
                    for (int j = i; j >= 0; j--) {
                        f[t][i] = Math.min(f[t][i], Math.max(f[t - 1][j], sum));
                        if (j > 0) sum += pages[j - 1];
                    }
                }
            }
            return f[k][n];
        }

方法2:二分

//437.书籍复印
public int copyBooks(int[] pages, int k) {
    //当人数够的时候,恰好有>=len(pages)的人数,只需要每个人抄写一本书即可,这时候最慢的抄书的人花费的时间恰好是最多的那本书的页数
    //当总数为tot时,这时候如果只有一个人抄书,需要这个人抄完整合pages列表
    int maxx = 0, tot = 0;
    for (int x : pages) {
        maxx = Math.max(maxx, x);
        tot += x;
    }
    int start = maxx, end = tot;
    //退出条件是start+1 =end start比end少一个
    while (start + 1 < end) {
        int mid = start + (end - start) / 2;
        if (countWorkers(pages, mid) <= k) {//说明当前的mid可能是需要的值(mid...end]排除
            end = mid;
        } else {
            start = mid + 1;//当前的count>k,说明该count不满足条件,排除[start...mid]这部分
        }
    }
    //判断下start
    if (countWorkers(pages, start) <= k) return start;
    //start不是目标值的话,就返回end
    return end;
}

//给定每个人的工作时长t,每个人都不能超过这个时长,返回需要多少个人能完成所有的工作
public int countWorkers(int[] pages, int t) {
    int sum = 0;
    int count = 0;
    for (int x : pages) {
        if (x + sum > t) {//如果当前的sum+x比t大,证明这个阶段的人抄书已经到顶了,重新开始并计数
            sum = x;
            count++;
        } else {
            sum += x;
        }
    }
    count++;
    return count;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值