业务模型
给定一个数组arr,arr[i]代表第i号咖啡机泡一杯咖啡的时间
给定一个正数N,表示N个人等着咖啡机泡咖啡,每台咖啡机只能轮流泡咖啡
只有一台咖啡机,一次只能洗一个杯子,时间耗费a,洗完才能洗下一杯
每个咖啡杯也可以自己挥发干净,时间耗费b,咖啡杯可以并行挥发
假设所有人拿到咖啡之后立刻喝干净,
返回从开始等到所有咖啡机变干净的最短时间
三个参数:int[] arr、int N,int a、int b
京东面试题~~
总结这种调度问题,首先分析一下问题是什么,比如这道题的最有调度问题针对的是:如何排队能尽量的以最少的
时间买完咖啡。然后确定一下已知量:有一个数组arr里面存放着每个咖啡机做一杯咖啡需要的时间,假设 1 ,3, 7
策略可以是让多的人在快咖啡机前排队,让少的人在慢咖啡机前排队,(因为慢咖啡机等待的时间长,等待的这段时间
快咖啡机都做成好几杯了),然后开始确定具体到底需要多少人在快咖啡机前,多少人在慢咖啡机前。
可以采用排序的方法,排序的元素是:(x , y),x表示的是:哪个时间点开始,y表示的是制作已被咖啡的时间。由于我
需要频发的改动x值,并且需要频发的添加与提取,所以采用小根堆的方法是最便捷的。
public static class Machine {
private int startTime;
private int runTime;
public Machine(int startTime, int runTime) {
this.startTime = startTime;
this.runTime = runTime;
}
}
public static class MyComparatoer implements Comparator<Machine> {
@Override
public int compare(Machine o1, Machine o2) {
return (o1.startTime + o1.runTime) - (o2.startTime + o2.runTime);
}
}
//arr[i] -- 第I号机器泡一杯咖啡所需要的时间。
//n -- 有N个人
//a -- 洗刷机洗1个杯子所需要的时间
//b -- 挥发所需时间
//返回从开始等到所有咖啡机变干净的最短时间
public static int minTime1(int[] arr, int n, int a, int b) {
PriorityQueue<Machine> heap = new PriorityQueue<>(new MyComparatoer());
//初始化小根堆。
for (int i = 0; i < arr.length; i++) {
heap.add(new Machine(0, arr[i]));
}
int[] drinks = new int[n];//存储着每个顾客买完咖啡后的时间
for (int i = 0; i < n; i++) {
Machine machine = heap.poll();
machine.startTime += machine.runTime;
heap.add(machine);
drinks[i] = machine.startTime;
}
//drinks一定是由低到高排序的。
//drinks中存储着刷碗的时间。由低到高排序的。
return bestTime(drinks, a, b, 0, 0);
}
//drinks:所有杯子可以开始洗的时间
//wash:洗一个杯子的时间
//air:挥发一个杯子的时间
//index:当前轮到哪个杯子
//free:洗的机器什么时间空闲
//drinks[0 ... ... ] 到全部洗干净所用的最少时间
public static int bestTime(int[] drinks, int wash, int air, int index, int free) {
if (index == drinks.length) {
return 0;
}
// index号杯子 决定洗
//轮到当前杯子时,洗刷机可能还没轮到当前杯子,他还忙。 -- free = 10 drinks[index] = 8
//轮到当前杯子时,洗刷机而可能空闲,但是当前杯子开始清洗的时间比较晚。 -- free = 7 drinks[index] = 9
//所以取两者的最大值才是开始清洗的时间。
int curWash = Math.max(free, drinks[index]) + wash;
int restWash = bestTime(drinks, wash, air, index + 1, curWash);
//curWash当前清洗的时间可能比restWash剩余清洗时间要长,也可能要短,但总之我要的是最终全部洗完的时间所以取最大值。
int p1 = Math.max(curWash, restWash);
// index号杯子 决定挥发
int curAir = drinks[index] + air;
int restAir = bestTime(drinks, wash, air, index + 1, free);
int p2 = Math.max(curAir, restAir);
return Math.min(p1, p2);
}
public static int minTime2(int[] arr, int n, int a, int b) {
PriorityQueue<Machine> heap = new PriorityQueue<>(new MyComparatoer());
//初始化小根堆。
for (int i = 0; i < arr.length; i++) {
heap.add(new Machine(0, arr[i]));
}
int[] drinks = new int[n];//存储着每个顾客买完咖啡后的时间
for (int i = 0; i < n; i++) {
Machine machine = heap.poll();
machine.startTime += machine.runTime;
heap.add(machine);
drinks[i] = machine.startTime;
}
return bestTimeDp(drinks, a, b);
}
//drinks -- 开始刷碗时间的顺序数组
//wash -- 刷一个碗的时间
//air -- 挥发的时间
public static int bestTimeDp(int[] drinks, int wash, int air) {
int N = drinks.length;
//变化的参数主要是: index, free -- 洗的机器什么时候空闲
//确定变化范围 index -- [0 , N] free -- [0 , maxFree]
//确定maxFree值
int maxFree = 0;
for (int i = 0; i < N; i++) {
maxFree = Math.max(drinks[i], maxFree) + wash;
}
int[][] dp = new int[N + 1][maxFree + 1];
//根据暴力递归分析得:应该从下往上构建
for (int index = N - 1; index >= 0; index--) {
for (int free = 0; free <= maxFree; free++) {
// index号杯子 决定洗
int curWash = Math.max(free, drinks[index]) + wash;
if (curWash > maxFree) { //如果中间过程中出现了大于maxFree的情况一定是错误的。以后的也都是错误的直接跳过。为什么是错误的,curwash -- 当前杯子涮完的时间/下一次杯子开始刷的时间,这个时间一定不断的增大的,如果中间过程中出现了大于maxFree的情况,说明这个free直接是错误的,正确的一定都在maxFree范围内。
break;
}
//int restWash = dp[index+1][curWash]可能出现越界,因为free趋于maxFree时,加wash会越界。
int restWash = dp[index + 1][curWash];
int p1 = Math.max(curWash, restWash);
// index号杯子 决定挥发
int curAir = drinks[index] + air;
int restAir = dp[index + 1][free];
int p2 = Math.max(curAir, restAir);
dp[index][free] = Math.min(p1, p2);
}
}
return dp[0][0];
}
业务模型的特点
if (curWash > maxFree) { //如果中间过程中出现了大于maxFree的情况一定是错误的。以后的也都是错误的直接跳过。为什么是错误的,curwash -- 当前杯子涮完的时间/下一次杯子开始刷的时间,这个时间一定不断的增大的,如果中间过程中出现了大于maxFree的情况,说明这个free直接是错误的,正确的一定都在maxFree范围内。
break;
}