【OD机试题笔记】员工派遣

员工派遣问题求解

题目描述

某公司部门需要派遣员工去国外做项目。

现在,代号为 x 的国家和代号为 y 的国家分别需要 cntx 名和 cnty 名员工。

部门每个员工有一个员工号(1,2,3,…),工号连续,从1开始。

部长派遣员工的规则:

规则1:从 [1, k] 中选择员工派遣出去
规则2:编号为 x 的倍数的员工不能去 x 国,编号为 y 的倍数的员工不能去 y 国。
问题:

找到最小的 k,使得可以将编号在 [1, k] 中的员工分配给 x 国和 y 国,且满足 x 国和 y 国的需求。

输入描述
四个整数 x,y,cntx,cnty。

2 ≤ x < y ≤ 30000
x 和 y 一定是质数
1 ≤ cntx, cn

输出描述
满足条件的最小的k

** 用例
输入:
2 3 3 1

输出:
5

说明:
2 表示国家代号2
3 表示国家代号3
3 表示国家2需要3个人
1 表示国家3需要1个人

思路

  1. 算法选型:二分查找(核心)+ 容斥原理(数学辅助),解决「找最小k满足分配条件」的问题;
  2. 问题转化
    • 二分枚举k值,判断[1,k]内员工能否满足x国、y国的人数需求;
    • 用容斥原理计算[1,k]内三类员工数:仅能去x国、仅能去y国、既能去x也能去y国;
  3. 核心计算(容斥原理)
    • 仅去x国:y的倍数 - x*y的倍数(y倍数不能去y国,且非x倍数可去x国);
    • 仅去y国:x的倍数 - x*y的倍数(x倍数不能去x国,且非y倍数可去y国);
    • 通用员工:k - x的倍数 - y的倍数 + x*y的倍数(非x/y倍数,可灵活分配);
  4. 条件判断:需从通用员工中补充的x、y国人数之和 ≤ 通用员工数,即可满足需求;
  5. 二分逻辑
    • 左边界:cntx + cnty(理论最小k),右边界:max(cntyx, cntxy)(安全上界);
    • 满足条件则尝试更小k(缩小右边界),不满足则增大k(扩大左边界),最终取最小满足条件的k。

时空复杂度

  • 时间复杂度:O(log(max_k)),其中max_k为二分右边界(max(cntyx, cntxy));
    二分迭代次数为对数级,每次迭代的check函数仅做常数次算术运算(O(1)),整体复杂度由二分主导;
  • 空间复杂度:O(1),仅使用常数个变量存储中间结果(无额外数据结构)。

代码

function solution() {
    const [x, y, cntx, cnty] = readline().split(' ').map(Number);
    
    // 计算1~k中num的倍数的数量
    const countMultiples = (k, num) => Math.floor(k / num);
    
    const check = (k) => {
        // 总人数:k
        // x的倍数:A = floor(k/x);y的倍数:B = floor(k/y)
        // x和y的公倍数(x*y的倍数,因x、y是质数):C = floor(k/(x*y))
        const A = countMultiples(k, x);
        const B = countMultiples(k, y);
        const C = countMultiples(k, x * y);
        
        // 仅能去x国:是y的倍数但不是x的倍数(B - C)
        const onlyX = B - C;
        // 仅能去y国:是x的倍数但不是y的倍数(A - C)
        const onlyY = A - C;
        // 既能去x也能去y:总人数 - 是x的倍数 - 是y的倍数 + 公倍数(容斥原理)
        const both = k - A - B + C;
        
        // 需满足三个条件:
        // 1. 仅x + 灵活分配的 >= cntx
        // 2. 仅y + 灵活分配的 >= cnty
        // 3. 总可用人数 >= cntx + cnty
        const needX = Math.max(0, cntx - onlyX);
        const needY = Math.max(0, cnty - onlyY);
        return (needX + needY <= both) && (onlyX + both >= cntx) && (onlyY + both >= cnty);
    };

    // 二分查找边界:最小可能是max(需要的最少人数),最大可设为cntx + cnty的x*y倍(足够大)
    let l = 1;
    let r = Math.max(cnty * x, cntx * y); // 安全的上界
    let ans = r;
    
    while (l <= r) {
        const m = l + Math.floor((r - l) / 2);
        if (check(m)) {
            ans = m;
            r = m - 1;
        } else {
            l = m + 1;
        }
    }
    
    console.log(ans);
}

// 测试用例
const cases = [
    `2 3 3 1`, // 输出5(正确)
    `2 3 3 3`  // 正确输出7(原代码会错误返回6)
];

let caseIndex = 0, lineIndex = 0;
const readline = (() => {
    let lines = [];
    return () => {
        if (!lineIndex) lines = cases[caseIndex].trim().split('\n').map(l => l.trim());
        return lines[lineIndex++];
    };
})();

cases.forEach((_, i) => {
    caseIndex = i;
    lineIndex = 0;
    solution();
    console.log('-------');
});
### 华为 OD 试题及解答 #### Java 题目解析 在华为 OD 考中,Java 是一种常见的编程语言。以下是一个关于二进制位操作的经典题目及其解决方案。 给定一个整数 `num`,计算其二进制表示中有多少个 `1` 的方法可以通过逐位检查实现[^2]。 以下是具体的代码示例: ```java import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int num = in.nextInt(); // 输入数字 int count = 0; // 计数器初始化 for (int i = 0; i < 32; i++) { // 假设输入的是标准的 32 位整数 if ((num & 1) == 1) { // 如果当前最低位是 1,则增加计数 count++; } num = num >>> 1; // 将数字无符号右移一位 } System.out.println(count); // 输出最终的结果 } } ``` 此程序的核心在于利用按位与运算符 (`&`) 和无符号右移运算符 (`>>>`) 来逐步提取并统计二进制中的每一位是否为 `1`。 --- #### 字符串处理题目解析 另一个常见问题是字符串的操作,比如将字符串中的每个单词首字母大写化[^3]。下面是一段 Python 实现的例子: ```python while True: try: words = input().split() # 获取用户输入并分割成列表 result = [] for word in words: if len(word) > 0: # 确保单词长度大于零 capitalized_word = f"{word[0].upper()}{word[1:]}" result.append(capitalized_word) print(" ".join(result)) # 打印结果 except Exception as e: break # 捕获异常后退出循环 ``` 该脚本通过遍历每一个单词,并将其第一个字符转为大写字母来完成任务。 --- #### 准备建议 为了更好地应对华为 OD 试,可以采取如下策略: - **熟悉常用算法**:掌握基本的数据结构(数组、链表、堆栈等)以及经典算法(排序、查找等),这些知识点经常会在实际测试中被考察到。 - **多练习编码能力**:针对不同类型的题目进行专项训练,尤其是时间复杂度优化方面的技巧学习。 - **理解业务场景需求**:部分试题可能结合具体应用场景设计,因此除了技术层面外还需要考虑逻辑思维的应用。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值