问题描述
小R在教室里有N名学生和M名教职员工。每个教职员工的评估速度不同,评估每个学生所需的时间不同。你得到一个数组A,其中A[i]表示第i名教职员工对一名学生进行评估所需的时间(以分钟为单位)。
教职员工需要对所有学生进行评估,每名教职员工在任意时刻只能评估一名学生。评估完成后,教职员工可以立即开始评估下一名学生。你的任务是找到在这些条件下评估所有学生所需的最短总时间。
测试样例
样例1:
输入:
N = 2, M = 2, A = [3, 4]
输出:4
样例2:
输入:
N = 3, M = 2, A = [5, 7]
输出:10
解题思路
为了找到评估所有学生所需的最短总时间,我们可以使用二分查找来优化搜索过程。二分查找是一种高效的搜索算法,适用于有序数组或在有界范围内查找目标值的情况。在这个问题中,我们可以将时间范围视为有界范围,通过二分查找来找到最小的满足条件的时间。
具体来说,我们需要定义一个检查函数 check(x),用于判断在给定的时间 x 内是否可以评估完所有学生。如果可以,我们尝试找更短的时间;如果不可以,我们增加时间范围。通过不断调整时间范围,我们最终可以找到评估所有学生所需的最短总时间。
具体步骤
- 初始化搜索范围:
• 左边界
left初始化为 0。 • 右边界right初始化为( N \times \text{max}(A) ),即所有学生都由最慢的教职员工评估所需的最大时间。 - 二分查找:
• 计算中间值
mid:mid = left + (right - left) / 2。 • 调用检查函数check(mid, N, M, A),判断在mid分钟内是否可以评估完所有学生。 • 如果可以,更新答案res为mid,并尝试找更短的时间(调整右边界为mid - 1)。 • 如果不可以,增加左边界为mid + 1。 - 检查函数
check(x): • 初始化总学生数totalStudents为 0。 • 遍历每个教职员工,计算每个教职员工在 x 分钟内可以评估的学生数,并累加到totalStudents。 • 如果总学生数大于或等于N,返回true;否则返回false。
完整代码:
import java.util.Arrays;
public class Main {
public static int solution(int N, int M, int[] A) {
int left = 0;
int right = N * Arrays.stream(A).max().getAsInt();
int res = right;
while (left <= right) {
int mid = left + (right - left) / 2;
if (check(mid, N, M, A)) {
res = mid;
right = mid - 1;
} else {
left = left + 1;
}
}
return res;
}
private static boolean check(int x, int N, int M, int[] A) {
int totalStudents = 0;
for (int i = 0; i < M; i++) {
totalStudents = totalStudents + x / A[i];
if (totalStudents >= N) {
return true;
}
}
return false;
}
public static void main(String[] args) {
System.out.println(solution(2, 2, new int[] { 3, 4 }) == 4);
System.out.println(solution(3, 2, new int[] { 5, 7 }) == 10);
System.out.println(solution(5, 3, new int[] { 2, 3, 5 }) == 6);
}
}
总结
通过将问题转化为在有界范围内查找目标值,我们可以大大减少搜索的时间复杂度,从而提高算法的效率。通过定义检查函数 check(x),我们可以将复杂的问题分解为多个简单的子问题。每次调用 check(x) 函数时,我们只需要计算每个教职员工在给定时间内可以评估的学生数,并累加这些数量,进而达到求解目的。