最近在准备华为OD机考时遇到一道关于部门人力分配的算法题,题目场景非常贴近实际开发中的资源规划问题。这道题要求我们在限定时间内完成多个需求开发,同时合理分配固定人力,确保每个月最多处理两个需求且工作量不超负荷。
核心约束条件可以归纳为:
这本质上是一个在约束条件下寻找最小值的问题。我们需要找到最小的人力值K,使得:
这类问题通常适合使用二分查找来解决,因为:
关键是如何验证某个K值是否可行:
java复制import java.util.Arrays;
import java.util.Collections;
public class Main {
public static void main(String[] args) {
// 示例输入处理
int M = 3;
int[] requirements = {3, 5, 3, 4};
System.out.println(minManpower(M, requirements));
}
public static int minManpower(int M, int[] requirements) {
// 将需求转为Integer数组以便降序排序
Integer[] reqs = Arrays.stream(requirements).boxed().toArray(Integer[]::new);
Arrays.sort(reqs, Collections.reverseOrder());
int left = reqs[0]; // 最小可能值:最大单个需求
int right = Arrays.stream(requirements).sum(); // 最大可能值:所有需求总和
while (left < right) {
int mid = left + (right - left) / 2;
if (canComplete(reqs, M, mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
private static boolean canComplete(Integer[] reqs, int M, int K) {
int months = 0;
int left = 0, right = reqs.length - 1;
while (left <= right) {
if (reqs[left] + reqs[right] <= K) {
left++;
right--;
} else {
left++;
}
months++;
}
return months <= M;
}
}
python复制def min_manpower(M, requirements):
reqs = sorted(requirements, reverse=True)
left, right = reqs[0], sum(requirements)
def is_possible(K):
months = 0
i, j = 0, len(reqs) - 1
while i <= j:
if reqs[i] + reqs[j] <= K:
i += 1
j -= 1
else:
i += 1
months += 1
return months <= M
while left < right:
mid = (left + right) // 2
if is_possible(mid):
right = mid
else:
left = mid + 1
return left
# 示例使用
M = 3
requirements = [3, 5, 3, 4]
print(min_manpower(M, requirements)) # 输出6
cpp复制#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;
bool canComplete(vector<int>& reqs, int M, int K) {
int months = 0;
int left = 0, right = reqs.size() - 1;
while (left <= right) {
if (reqs[left] + reqs[right] <= K) {
left++;
right--;
} else {
left++;
}
months++;
}
return months <= M;
}
int minManpower(int M, vector<int>& requirements) {
vector<int> reqs = requirements;
sort(reqs.rbegin(), reqs.rend());
int left = reqs[0];
int right = accumulate(reqs.begin(), reqs.end(), 0);
while (left < right) {
int mid = left + (right - left) / 2;
if (canComplete(reqs, M, mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
int main() {
int M = 3;
vector<int> requirements = {3, 5, 3, 4};
cout << minManpower(M, requirements) << endl; // 输出6
return 0;
}
python复制# 用例1:刚好满足最小人力
M = 3
requirements = [3, 5, 3, 4]
# 预期输出:6
# 用例2:所有需求都需要单独处理
M = 4
requirements = [7, 8, 9, 10]
# 预期输出:10
# 用例3:可以完美配对
M = 2
requirements = [4, 4, 4, 4]
# 预期输出:8
当M = N时(每个需求单独处理):
当M = N/2时(所有需求都能配对):
超大需求值:
提前终止条件:
并行计算:
动态需求场景:
人力波动情况:
多资源类型:
提示:在实际面试中,除了正确实现算法外,面试官通常还会考察对问题边界条件的考虑、代码的可读性以及时间/空间复杂度的分析能力。建议在练习时养成写注释和测试用例的习惯。
排序方向错误:
二分查找边界:
月份计数错误:
打印中间结果:
小规模测试:
边界测试:
我在实际编码中发现,最容易出错的是双指针的移动逻辑。一个实用的调试方法是手动模拟一个小例子:
例如requirements = [5,3,3,4], M=3, K=6:
这个例子说明初始算法可能存在问题,需要调整验证逻辑。正确的做法应该是:
这说明我们需要更精确地计算月份数,不能简单地对每个操作都增加月份计数。