题目描述
某公司部门需要派遣员工去国外做项目。
现在,代号为 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个人
思路
- 算法选型:二分查找(核心)+ 容斥原理(数学辅助),解决「找最小k满足分配条件」的问题;
- 问题转化:
- 二分枚举k值,判断
[1,k]内员工能否满足x国、y国的人数需求; - 用容斥原理计算
[1,k]内三类员工数:仅能去x国、仅能去y国、既能去x也能去y国;
- 二分枚举k值,判断
- 核心计算(容斥原理):
- 仅去x国:y的倍数 - x*y的倍数(y倍数不能去y国,且非x倍数可去x国);
- 仅去y国:x的倍数 - x*y的倍数(x倍数不能去x国,且非y倍数可去y国);
- 通用员工:k - x的倍数 - y的倍数 + x*y的倍数(非x/y倍数,可灵活分配);
- 条件判断:需从通用员工中补充的x、y国人数之和 ≤ 通用员工数,即可满足需求;
- 二分逻辑:
- 左边界: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('-------');
});
员工派遣问题求解
928

被折叠的 条评论
为什么被折叠?



