21.3:买咖啡与刷杯子问题

业务模型

给定一个数组arr,arr[i]代表第i号咖啡机泡一杯咖啡的时间

给定一个正数N,表示N个人等着咖啡机泡咖啡,每台咖啡机只能轮流泡咖啡

只有一台咖啡机,一次只能洗一个杯子,时间耗费a,洗完才能洗下一杯

每个咖啡杯也可以自己挥发干净,时间耗费b,咖啡杯可以并行挥发

假设所有人拿到咖啡之后立刻喝干净,

返回从开始等到所有咖啡机变干净的最短时间

三个参数:int[] arr、int N,int a、int b

京东面试题~~

21.3:买咖啡与刷杯子问题_第1张图片

总结这种调度问题,首先分析一下问题是什么,比如这道题的最有调度问题针对的是:如何排队能尽量的以最少的

时间买完咖啡。然后确定一下已知量:有一个数组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;
 }

你可能感兴趣的:(算法,java)