【Py/Java/C++三种语言OD2023C卷真题】20天拿下华为OD笔试之【二分查找】2023C-项目排期【欧弟算法】全网注释最详细分类最全的华为OD真题题解

有LeetCode算法/华为OD考试扣扣交流群可加 948025485
可上全网独家的 欧弟OJ系统 练习华子OD、大厂真题
绿色聊天软件戳 od1336了解算法冲刺训练

题目描述与示例

题目描述

项目组共有N个开发人员,项目经理接到了M个独立的需求,每个需求的工作量不同,且每个需求只能由一个开发人员独立完成,不能多人合作。假定各个需求之间无任何先后依赖关系,请设计算法帮助项目经理进行工作安排,使整个项目能用最少的时间交付。

输入描述

第一行输入为M个需求的工作量,单位为天,用逗号隔开。

例如:X1 X2 X3 … Xm 。表示共有M个需求,每个需求的工作量分别为X1天,X2天…Xm天。

其中0 < M < 300 < Xm < 200

第二行输入为项目组人员数量N

输出描述

最快完成所有工作的天数

示例

输入

6 2 7 7 9 3 2 1 3 11 4
2

输出

28

说明

共有两位员工,其中一位分配需求 6 2 7 7 3 2 1 共需要28天完成,另一位分配需求 9 3 11 4 共需要27天完成,故完成所有工作至少需要28天。

解题思路

又是一道描述相当晦涩的题目(很想骂人)

用比较简洁的数学语言来描述就是,将数组X1, X2, X3, ... ,Xm分为N部分(并非子数组,不要求连续),设每一部分的和为sum1, sum2, ..., sumN,要求找到一种分配方式使得max(sum1, sum2, ..., sumN)最小。

用一句简单的话来说,就是最小化各部分求和的最大值。这种设问一定要想到用二分来完成。

将问题转化为,我们需要找到一个阈值k(一个人的最大工作量),并将原数组nums可以被分为N部分(分配给N个人),使得这N部分的各自求和的最大值都不会超过k(每个人各自的工作量不会超过k)。

显然k的选择是一个二段性问题:

  • k非常大时,原数组nums无论如何分配都可以完成要求
  • k非常小时,原数组nums无论怎么分配都无法完成要求
  • 必然存在一个临界值k,使得存在nums的分配结果恰好完成要求。

我们希望找到这个阈值k,因此需要对k进行二分查找,二分查找的范围为[max(nums), sum(nums)]。当

  • k = max(nums)时,能够被分为m = len(nums)部分(需要m个人来完成所有工作)
  • k = sum(nums)时,能够被分为1部分(只由1个人可以完成所有工作)

而上述二分过程的贪心子问题为:当我们选择了阈值k时,数组nums能否被分割不超过N部分?

这个问题就和【贪心】2023B-数据最节约的备份方法几乎完全一致了,其代码为

 def sub_question(k, nums, m, N):
    ans = 0
    check = [0] * m
    for i in range(m):
        if check[i] == 1:
            continue
        ans += 1
        cur_sum = 0
        j = i
        while j < m:
            if check[j] == 1:
                j += 1
                continue
            if nums[j] + cur_sum > k:
                j += 1
            else:
                cur_sum += nums[j]
                check[j] = 1
                j += 1
    return ans <= N

初始化左闭右开区间left = max(nums)right = sum(nums) + 1,进行二分。

计算mid = (left + right) // 2。当

  • sub_question(mid, nums, m, N)True时,说明当选择了阈值k = mid时,可以将任务分配给N个人(组数小于等于N)。此时的mid的选择偏大,区间应该左移,right左移。
  • sub_question(mid, nums, m, N)False时,说明当选择了阈值k = mid时,无法将任务分配给N个人(组数大于N)。此时的mid的选择偏小,区间应该右移,left右移。

故结合贪心子问题,整体的二分代码为

nums = list(map(int, input().split()))
m = len(nums)
N = int(input())
nums.sort(reverse = True)
left, right = max(nums), sum(nums)+1
while left < right:
    mid = (right + left) // 2
    if sub_question(mid, nums, m, N):
        right = mid
    else:
        left = mid + 1
        
print(left)

代码

python

# 题目:【二分查找】2023C-项目排期
# 分值:200
# 作者:许老师-闭着眼睛学数理化
# 算法:二分查找/贪心
# 代码看不懂的地方,请直接在群上提问
# 相关题目:【贪心】2023B-数据最节约的备份方法


# 贪心子问题,当选择了阈值k时,如果m个任务nums可以被分配给N个员工,
# 且每一个员工的工作总量不超过k则返回True,否则返回False
# (注意此处nums必须是一个已排序好的逆序数组)
# 该子问题的时间复杂度与nums的长度m相关,为O(m^2)
def sub_question(k, nums, m, N):
    ans = 0
    # 初始化长度为m的check数组,用来表示第i个任务是否已经分配给了某一个员工
    check = [0] * m
    # 遍历所有nums每一个工作量
    for i in range(m):
        # 如果该工作已经由某个员工完成了,则直接跳过
        if check[i] == 1:
            continue
        # 否则,需要一个新的员工
        # 来完成包含工作nums[i]在内的若干工作
        # 故ans更新
        ans += 1
        # 初始化当前员工所做的工作总量为0
        cur_sum = 0
        # 初始化变量j为i,用于修改当前这个员工的工作情况
        j = i
        # 进行内层循环,此处涉及贪心算法
        while j < m:
            # 如果第j个工作已经安排,则j直接递增,跳过第j个工作
            if check[j] == 1:
                j += 1
                continue
            # 如果第j份工作和当前员工之前的工作量之和超过k
            # 则这个员工不能选择这份工作,j递增
            if nums[j] + cur_sum > k:
                j += 1
            # 如果第j份工作和当前员工之前的工作量之和不超过k
            # 则贪心地将这份工作安排给这个员工
            # 修改cur_sum和check[j],j递增
            else:
                cur_sum += nums[j]
                check[j] = 1
                j += 1
    # 退出循环时,如果需要的人数ans不超过N,则返回True,否则返回False
    return ans <= N


# 输入m个任务构成的数组
nums = list(map(int, input().split()))
# 获得nums的长度,即任务数量
m = len(nums)
# 输入员工人数N
N = int(input())
# 对nums进行逆序排序,方便后续贪心子问题的计算
nums.sort(reverse = True)
# 初始化左闭右开
left, right = max(nums), sum(nums)+1
while left < right:
    mid = (right + left) // 2
    # 如果选择了mid作为阈值,可以将任务分配给N个人(组数小于等于N)
    # 说明mid的选择偏大,区间应该左移,right左移
    if sub_question(mid, nums, m, N):
        right = mid
    # 如果选择了mid作为阈值,无法将任务分配给N个人(组数多于N)
    # 说明mid的选择偏小,区间应该右移,left右移
    else:
        left = mid + 1

# 退出循环时,存在left = right是恰好可以将任务分配给N个人的阈值k
# left或right即为答案
print(left)

java

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    // 贪心子问题
    private static boolean subQuestion(int k, int[] nums, int m, int N) {
        int ans = 0;
        int[] check = new int[m];

        for (int i = 0; i < m; i++) {
            if (check[i] == 1) continue;
            ans++;
            int curSum = 0;
            int j = i;
            while (j < m) {
                if (check[j] == 1) {
                    j++;
                    continue;
                }
                if (nums[j] + curSum > k) {
                    j++;
                } else {
                    curSum += nums[j];
                    check[j] = 1;
                    j++;
                }
            }
        }
        return ans <= N;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 输入m个任务构成的数组
        String[] numsStr = scanner.nextLine().split(" ");
        int m = numsStr.length;
        int[] nums = new int[m];
        for (int i = 0; i < m; i++) {
            nums[i] = Integer.parseInt(numsStr[i]);
        }

        // 输入员工人数N
        int N = scanner.nextInt();

        // 对nums进行逆序排序
        Arrays.sort(nums);
        for (int i = 0; i < m / 2; i++) {
            int temp = nums[i];
            nums[i] = nums[m - i - 1];
            nums[m - i - 1] = temp;
        }

        // 初始化左闭右开
        int left = nums[0], right = Arrays.stream(nums).sum() + 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (subQuestion(mid, nums, m, N)) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }

        // 输出结果
        System.out.println(left);
    }
}

cpp

#include <iostream>
#include <vector>
#include <algorithm>
#include <sstream>
#include <numeric>

using namespace std;

bool subQuestion(int k, vector<int>& nums, int m, int N) {
    int ans = 0;
    vector<int> check(m, 0);

    for (int i = 0; i < m; i++) {
        if (check[i] == 1) continue;
        ans++;
        int curSum = 0;
        int j = i;
        while (j < m) {
            if (check[j] == 1) {
                j++;
                continue;
            }
            if (nums[j] + curSum > k) {
                j++;
            } else {
                curSum += nums[j];
                check[j] = 1;
                j++;
            }
        }
    }
    return ans <= N;
}

int main() {
    vector<int> nums;
    string input;
    getline(cin, input);
    stringstream ss(input);
    int num;
    while (ss >> num) {
        nums.push_back(num);
    }
    int m = nums.size();
    int N;
    cin >> N;

    sort(nums.begin(), nums.end(), greater<int>());
    int left = nums[0], right = accumulate(nums.begin(), nums.end(), 0) + 1;
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (subQuestion(mid, nums, m, N)) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }

    cout << left << endl;
    return 0;
}

时空复杂度

时间复杂度:O(log(C)m^2)。贪心子问题的时间复杂度为O(m^2),二分查找的时间复杂度为O(logC),其中C = sum(nums) - max(nums)

空间复杂度:O(m)。贪心子问题需要构建长度为mcheck数组。


华为OD算法/大厂面试高频题算法练习冲刺训练

  • 华为OD算法/大厂面试高频题算法冲刺训练目前开始常态化报名!目前已服务300+同学成功上岸!

  • 课程讲师为全网50w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化

  • 每期人数维持在20人内,保证能够最大限度地满足到每一个同学的需求,达到和1v1同样的学习效果!

  • 60+天陪伴式学习,40+直播课时,300+动画图解视频,300+LeetCode经典题,200+华为OD真题/大厂真题,还有简历修改、模拟面试、专属HR对接将为你解锁

  • 可上全网独家的欧弟OJ系统练习华子OD、大厂真题

  • 可查看链接 大厂真题汇总 & OD真题汇总(持续更新)

  • 绿色聊天软件戳 od1336了解更多

  • 16
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
IT开发表格排期模板是指一种用于管理IT开发项目时间安排的模板。在IT开发过程中,合理而准确地安排时间是确保项目能够按时交付的关键。表格排期模板可帮助团队成员了解项目所需的工作量时间,并提前识别潜在的风险和延迟。 一个典型的IT开发表格排期模板包括以下几个重要部分: 1. 项目概况:包括项目名称、项目经理、起止日期等基本信息。 2. 里程碑:列出项目的关键里程碑,这些里程碑是项目成功的关键节点。每个里程碑应包括开始日期、完成日期以及与之相关的任务和工作内容。 3. 任务列表:将项目分解为具体的任务,并为每个任务指定负责人和预计完成日期。这有助于明确每个人的责任,并确保项目按计划进行。 4. 依赖关系:展示项目中各个任务之间的依赖关系。这能够帮助团队成员了解哪些任务必须在其他任务完成后开始,避免出现不必要的延迟。 5. 资源分配:记录项目中可用资源的分配情况,包括人力资源、设备和资金。这有助于确保项目资源的合理利用和调配。 6. 风险评估和管理:对可能导致项目延迟的风险进行评估,并制定相应的风险应对策略。这有助于项目团队及时应对潜在问题,降低项目风险。 通过IT开发表格排期模板,团队成员可以清晰地看到项目时间安排,知道自己的工作内容和截止日期。同时,项目经理也可以通过模板来跟踪项目进度,及时调整资源分配和解决潜在问题。 总之,IT开发表格排期模板是IT项目管理中非常重要的工具,它能够帮助团队成员明确目标、合理分配资源、减少延迟风险,确保项目按计划顺利进行

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值