zoukankan      html  css  js  c++  java
  • 二分入门练习题6 最大值最小化 题解

    题目描述

    在印刷术发明之前,复制一本书是一个很困难的工作,工作量很大,而且需要大家的积极配合来抄写一本书,团队合作能力很重要。当时都是通过招募抄写员来进行书本的录入和复制工作的, 假设现在要抄写 (m) 本书,编号为 (1,2,3...m) , 每本书有 (1 le x le 100000) 页, 把这些书分配给 (k) 个抄写员,要求分配给某个抄写员的那些书的编号必须是连续的,每本书只能被一个抄写员抄写。每个抄写员的速度是相同的,你的任务就是找到一个最佳的分配方案,使得所有书能够被抄完的前提下,每个抄写员所抄写的页数最少。

    输入格式

    在第一行中,有两个整数 (m 和 k, 1<=k,m<=100000) 。 在第二行中,有 (m) 个整数 (x_i) 用空格分隔。 所有这些值都为正且小于 (100000)

    输出格式

    输出一行数字,代表最佳的分配方案中,全部抄写完毕,抄写页数最多的抄写员所抄写的页数。

    样例输入

    9 3
    100 200 300 400 500 600 700 800 900
    

    样例输出

    1700
    

    题目分析

    此题涉及算法:二分。
    视频讲解地址:https://www.bilibili.com/video/av59514881/?p=3
    这道题目是二分答案。
    首先我们可以编写一个 bool check(int num) 函数来判断在分配给每个抄写员 (num) 页书时 (k) 个抄写员是否能够超写完 (m) 本书。如果可以,(check(num)) 返回 true;否则,(check(num)) 返回 false
    然后,我们会发现:

    • (num = 0) 时,(check(0)) 肯定返回 false
    • (num = sum_{i=1}^nx_i) 时,(check(sum_{i=1}^nx_i)) 肯定返回 true,因为此时只需要一个抄写员就可以抄写完 (m) 本书。

    那么在 (0)(sum_{i=1}^nx_i) 之间肯定存在一个最小的 (num) 使得 (check(num)) 返回 true

    我们可以发现:

    • 如果 (check(num)) 返回 true ,那么 (check(num+1)、check(num+2)、……、check(sum_{i=1}^nx_i)) 都返回 true
    • 如果 (check(num-1)) 返回 false ,那么 (check(num-2)、check(num-3)、……、check(0)) 都返回 false

    所以如果我们以答案 num 为自变量,以答案是否成立((check(num)) 返回的结果)为应变量,我们可以得到一个应变量随自变量变化的单调函数曲线。

    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 100010;
    int m, k;   // m表示书有多少本,k表示抄写员的数量
    long long a[maxn];  // a[i]表示第i本书的页数
    // check函数用于判断分配给没个抄写员num页书的时候能否超写完
    bool check(long long num) {
        int id = 1;     // id用于标记当前抄写员的编号,一开始id=1
        long long tot = 0;  // tot用于表示第id个抄写员目前已经抄写的书的页数
        for (int i = 0; i < m; i ++) {  // 循环遍历每一本书
            if (num - tot >= a[i]) {    // 如果第id个抄写员能够抄写第i本书,
                tot += a[i];        // 将tot+=a[i],因为tot表示第id个抄写员目前抄写的书
            }
            else if (a[i] > num)    // 如果a[i]>num,说明任何抄写员都抄写不了第i本书
                return false;
            else {
                id ++;  // id++,变成下一个抄写员的编号
                tot = a[i]; // 更新下一个抄写员已经抄写的书的页数为a[i],因为他目前只抄写了第i本书
                if (id > k) return false; // 如果id>k,说明k个抄写员已经不够用了,直接返回false
            }
        }
        return true;    // 返回true
    }
    int main() {
        cin >> m >> k;
        long long sum = 0;
        for (int i = 0; i < m; i ++) {
            cin >> a[i];
            sum += a[i];
        }
        long long L = 0, R = sum, res; // L记录答案的左边界,R记录答案的右边界,res记录答案
        while (L <= R) {
            long long mid = (L + R) / 2LL;
            if (check(mid)) {   // 找到当前最优解
                res = mid;      // 更新答案
                R = mid - 1;    // 看看有没有更优解
            }
            else L = mid + 1;   // 进有半部分找解
        }
        cout << res << endl;    // 输出答案
        return 0;
    }
    
  • 相关阅读:
    朋友面试被问到---静态构造函数
    (设计模式之一)浅析简单工厂模式
    out与ref修饰符
    图解引用类型
    图解值类型
    PHP之路---1---Wamp环境配置--php环境配置
    js遮罩层弹出框
    总结
    PSP记录个人项目耗时情况
    代码复审
  • 原文地址:https://www.cnblogs.com/zifeiynoip/p/11450631.html
Copyright © 2011-2022 走看看