关闭

抄书问题

标签: 动态规划
223人阅读 评论(0) 收藏 举报
分类:

题目描述

有n本书和k个抄写员。要求n本书必须连续地分配给这k个抄写员抄写。也就是说前a1本书分给第一个抄写员,接下来a2本书分给第二个抄写员,如此类推(a1,a2需要你的算法来决定)。给定n,k和每本书的页数p1,p2..pn,假定每个抄写员速度一样(每分钟1页),k个抄写员同时开始抄写,问最少需要多少时间能够将所有书全部抄写完工?
样例输入 Sample Input
9 3
1 2 3 4 5 6 7 8 9
样例输出 Sample Output
1 5
6 7
8 9

题目解答

解题思路

  • 算法的思想:动态规划的过程
    举例:有2个人 , 4本书( 4 1 5 2)
    1 2 3 4
    1 4 5 10 12
    2 4 4 5 7
    求f[2][4]的时候(注意抄书是并行的, 要取最大值)
    f[1][4] sum(4)-sum(4) 前面1个人完成4本书
    f[1][3] sum(4)-sum(3) 前面1个人完成3本书
    f[1][2] sum(4)-sum(2) 前面1个人完成2本书
    f[1][1] sum(4)-sum(1) 前面1个人完成1本书
    f[1][0] sum(4)-sum(0) 前面1个人完成0本书

  • 二分法思想

代码实现

动态规划

 public int shortestTime(int[] pages, int k) {

        if(pages == null || pages.length == 0)
            return -1;
        //n本书
        int n = pages.length;
        int[][] dp = new int[k+1][n+1];
        int[] sum = new int[n+1];

        sum[1] = pages[0];
        dp[1][1] = pages[0];
        for(int i = 2; i <= n; i++){
            sum[i] = sum[i-1]+pages[i-1];
            dp[1][i] = sum[i];
        }


        //k个人
        for(int i = 2; i <= k; i++){
            for(int j = 1; j <= n; j++){

                int minValue = Integer.MAX_VALUE;
                for(int jj = 0; jj <= j; jj++){
                    int max = Math.max(dp[i-1][j-jj], sum[j]-sum[j-jj]);
                    if(max < minValue){
                        minValue = max;
                    }
                }
                dp[i][j] = minValue;
            }
        }

        return dp[k][n];
    }

二分法

     /**
     * 二分法思想
     */
    public int shorestDichotomy(int[] pages, int k){

        if(pages == null || pages.length == 0 || k == 0)
            return -1;

        int minPage = Integer.MAX_VALUE;
        int totalPage = 0;
        for(int page : pages){
            totalPage += page;
            minPage = Math.min(minPage, page);
        }

        int left = minPage, right = totalPage;
        while(left < right){
            int middle = (left+right) / 2;
            if(check(pages, k, middle))
                right = middle;
            else
                left = middle + 1;
        }

        return left;

    }

    /**
     * 返回true表示要上调
     * 返回false表示要下调
     * 二分一个单人抄书的最大值,然后从后向前让每个人尽可能多抄->不多于二分的值
     * 若抄完了整本书,则下调上界->即可能人没轮完就取完了
     * 若还未抄完整本书就轮完了所有人,则上调下界-即可能还未取满就完了
     * 当上界=下界时退出,按同样的方法从后往前取书
     */
    public boolean check(int[] pages, int k, int middle){

        int j = 0;
        int len = pages.length;
        for(int person = 1; person <= k; person++){

            int sum = 0;
            while(j < len && (sum+pages[j]) <= middle){
                sum += pages[j];
                j++;
            }
            if(j == len)
                return true;
        }
        return false;
    }
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:41977次
    • 积分:1858
    • 等级:
    • 排名:千里之外
    • 原创:148篇
    • 转载:1篇
    • 译文:0篇
    • 评论:1条
    博客专栏
    最新评论