暴力递归到动态规划咖啡问题

package day_18;

import sun.security.util.Length;

import java.util.PriorityQueue;

/**
 * 给定一个数组arr,arr[i]代表第i号咖啡机泡一杯咖啡的时间
 * 给定一个正数N,表示N个人等着咖啡机泡咖啡,每台咖啡机只能轮流泡咖啡
 * 只有一台可以洗咖啡杯子的机器,一次只能洗一个杯子,时间耗费a,洗完才能洗下一杯
 * 每个咖啡杯也可以自己挥发干净,时间耗费b,咖啡杯可以并行挥发
 * 假设所有人拿到咖啡之后立刻喝干净,
 * 返回从开始等到所有咖啡机变干净的最短时间
 * 三个参数:int[] arr、int N,int a、int b
 * @Author huawei
 * @Date 2021/5/19 12:39
 * @Version 1.0
 */
public class Code03_Coffee {
    public static int coffee(int [] arr,int N,int a,int b){

        if(arr == null || arr.length == 0 || a <=0 || b <=0 ){
            return -1;
        }
        // 1.首先N个人需要先排队拿到咖啡,首先得找到N个人在多个咖啡机下排队所花费的一个最小时间
        // 需要准备一个小顶堆,这个小顶堆按照每个机器的可用时间+所花费时间来排序,说白了,就是我一个
        // 用户使用这台咖啡机需要等多久才能拿到咖啡,那肯定我们都会选择那个等待时间最短的咖啡机,这个小顶堆就是用来做
        // 这个事情的
        PriorityQueue<Machine> heap = new
                PriorityQueue<>((m1,m2)->{return (m1.startTime + m1.washTime) - (m2.startTime + m2.washTime);});
        for(int i = 0 ; i < arr.length ; i++){
            heap.add(new Machine(0,arr[i]));
        }
        int [] time  = new int[N];
        for(int i = 0 ; i < N ; i++){
            Machine machine = heap.poll();
            int start = machine.startTime;
            int washTime = machine.washTime;
            time[i] = start + washTime;
            heap.add(new Machine(start + washTime ,washTime));
        }
        // 2.得到每一个人的拿到咖啡之后的最优时间点数之后,我们就来使用自然智慧来憋暴力递归
        return process(time,a,b,0,0);
    }
    // drinks 每个人拿到咖啡的最优时间点
    // wash 洗一个咖啡杯所消耗的时间
    // air 一个咖啡杯挥发所消耗的时间
    // free 洗咖啡机机器的可用时间
    // 我只关心drink[0...index]这个时间段中的最优时间
    public static int process(int [] drink,int wash,int air,int index,int free){
        if(index == drink.length){
            // base case
            return 0;
        }
        // 分析可能性,洗咖啡杯只有两种可能性
        // 1.使用机器来洗咖啡杯(串行)
        // 如果使用机器来洗咖啡杯,我必须要等到机器空闲的时候才能洗,而且我也必须要等待我拿到了咖啡之后才能洗咖啡杯
        // 所以两者之间求一个最大值,才是我此刻能够洗咖啡杯的时间点
        int machineCleanTime = Math.max(drink[index],free) + wash;
        // 然后当前的这个人洗完咖啡杯了,应该轮到下一个人洗咖啡杯了(在当前这个人占用了机器的情况下,其他人洗咖啡杯所消耗的时间)
        int otherCleanTime = process(drink,wash,air,index + 1, machineCleanTime);
        // 注意,要在两者之间求一个最大值,因为都是洗咖啡杯所消耗的时间,且都要完成洗咖啡杯的工作
        machineCleanTime = Math.max(machineCleanTime,otherCleanTime);
        // 2.让咖啡杯自己挥发(并行)
        int airCleanTime = drink[index] + air;
        // 并行就是不对下一个造成任何的影响
        otherCleanTime = process(drink, wash, air, index + 1, free);
        // 3.求两个的最小值
        return Math.min(machineCleanTime,Math.max(airCleanTime,otherCleanTime));
    }




    // 两个可变参数
    // index [0 N]
    // free 不好评估,既然不好评估,就需要计算最坏情况,就是最差的情况下free的最大值是多少
    public static int dp(int [] arr,int wash,int air){
        if(arr == null || arr.length == 0 || wash <=0 || air <=0 ){
            return -1;
        }
        PriorityQueue<Machine> heap = new
                PriorityQueue<>((m1,m2)->{return (m1.startTime + m1.washTime) - (m2.startTime + m2.washTime);});
        for(int i = 0 ; i < arr.length ; i++){
            heap.add(new Machine(0,arr[i]));
        }
        int N = arr.length;
        int [] time  = new int[N];
        for(int i = 0 ; i < N ; i++){
            Machine machine = heap.poll();
            int start = machine.startTime;
            int washTime = machine.washTime;
            time[i] = start + washTime;
            heap.add(new Machine(start + washTime ,washTime));
        }
        // 需要求出机器空闲的最大时间
        int maxFree = 0;
        for(int i = 0 ; i < N ; i++){
            maxFree = Math.max(time[i],maxFree) + wash;
        }
        int [][] dp = new int[N + 1][maxFree + 1];
        return process2(time,air,wash,dp);
    }
    public static int process2(int [] drink,int air,int wash,int [][]dp){
        int N = drink.length;
        for(int index = N - 1; index >=0 ; index--){
            for(int free = 0 ; free < dp[0].length;free++){
                int machineCleanTime = Math.max(drink[index],free) + wash;
                if(machineCleanTime > dp[0].length - 1){
                    break;
                }
                int otherCleanTime = dp[index + 1][machineCleanTime];
                int p1 = Math.max(machineCleanTime,otherCleanTime);
                int airCleanTime = drink[index] + air;
                otherCleanTime = dp[index + 1][free];
                int p2 = Math.max(airCleanTime,otherCleanTime);
                dp[index][free] = Math.min(p1,p2);
            }
        }
        return dp[0][0];
    }



    private static class Machine{
        // 可以在几号时间点使用
        int startTime;
        // 这台咖啡机洗咖啡杯子的时间
        int washTime;

        public Machine(int startTime, int washTime) {
            this.startTime = startTime;
            this.washTime = washTime;
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值