华为的咖啡机们

小七首博!


本菜小声bb

  1. 小七,崇拜类人猿
  2. 近两天小七人生第一次被技术面、机试的爸爸们殴打之后,打算变成爸爸那样的人
  3. 于是乎有了这第一篇分享:关于昨晚做的华为机试-软件类-题3;当时没想出来,回去以后继续想了十几分钟有了一个大概的思路,到今天上午整理清思路来分享辽!
  4. 初来乍到,应该不晚!

情景带入

华父购得咖啡机若干,宾客纷至沓来,毕竟在打码闲暇时小酌一杯,实为人生一大乐事
这时,华父却说:
“既然要在我的地盘上撒野,就得按照我的方式来撒!”
具体细则如下:

  1. 每台咖啡器有自己的cook time
  2. 每台咖啡机一次只能做一杯咖啡,垃圾
  3. 喝咖啡的过程是透明的(秒喝)
  4. 喝了咖啡必须洗杯子不然会很臭,讲究
  5. 选择洗杯机洗杯子(时间x),或者神奇的等待咖啡残渣自己挥发(时间y)
  6. 我只有钱买一台洗杯机(多人需要排队等待)

题目要求

每个华父手下有小弟n人,求这n个小弟均品尝一番咖啡之后的最少时间 :P
并且把上述“华父买咖啡机,立破规矩”的过程复制若干次

  1. 输入第一行是指复制的次数(case数目)
  2. 第二行开始,每两行为一组
  3. 每组第一行表示该case下面的n m x y参数, 第二行表示这批咖啡机每台的工作时间
  4. 输出每个case的最少时间,换行 。
输入范例1
1
1 1 1 1
10
输出范例1
11

输入范例2
2
2 2 1 1
10 8
1 1 1 1
10
输出范例2
11
11

解题思路

· 昨晚在线答题的时候

æ¯”è¾ƒè¾“å…¥çš„éªŒè¯ç å’Œå®žé™…ç”Ÿæˆçš„éªŒè¯ç æ˜¯å¦ç›¸å

return null

“图书馆思考了俩小时了,该吃宵夜了”

· 回去经过十几分钟的思考

干他!


  • 每个case的n m x y 的配置抽象成为一个Coffee对象,计算该对象所对应的最短时间
  • 小弟的视角切入,每个小弟面临两个选择
    1. 用哪一台咖啡机可以最快开始享受人生
      得到该人等待咖啡机的时间和煮咖啡的时间
    2. 等杯子自己洗还是去洗杯子?
      得到该人等待洗杯器的时间和洗杯子的时间(自己洗就不用等待洗杯器)

如何确定这两个选择?
  1. 从时间0开始,到选择了一台咖啡机并且时间最短的那台咖啡机,具体如下
    1. initial 假设初始选择第0台咖啡机, minTime = wait(0) + cook(0), min = 0
    2. for i from 1 to m-1 咖啡机:
      if 某一台咖啡机所用的时间+等待时间 cook(i) + wait(i)<目前所选的咖啡机 cook(min) + wait(min) :
         min = i, minTime = wait(i) + cook(i);
      update 该小弟等待咖啡机的时间以及煮完咖啡的时间endTime
  2. 从时间最短的咖啡机工作完之后,洗杯机才进入自己的时间线,所以洗杯机开始时间记为耗时最短的咖啡机的工作时间:begin=min{cook(i)},具体如下:
    1. initial 假设等待洗杯机的时间wait:
      if begin+使用洗杯机的总人数*洗杯机的工作时间<该人煮完咖啡的时间,说明该人在煮完咖啡之前,洗杯机就空闲了,不用等待
        wait=0;
      else 还要等待洗杯机
        wait=begin+洗杯机工作人数*洗杯机的工作时间-该人煮完咖啡的时间;
    2. 决定等待洗杯机洗杯子还是自己挥发?
      if wait+x<y:等待洗杯机
        endTime += wait + x;
        使用洗杯机的总人数++;
      else 等待挥发
        endTime += y;
  3. 每个人做完选择之后,最短时间就是最大的endTime
  4. ** Done.**

两条时间线如何合并在一起

假设现在有3台咖啡机,4个小弟;画个小图解释一下:
首先先把两个等待时间线完全分开

Sat 06 12:00 Apr 07 12:00 Mon 08 12:00 Tue 09 12:00 Wed 10 12:00 Thu 11 12:00 Fri 12 小弟1 小弟2 小弟3 小弟4 小弟5 小弟6 现有任务 等待咖啡机

为了简便,就用甘特图简单展示一下情况,同种颜色的小弟,表示他们使用同一种咖啡机
类似地,对于洗杯机有:

Mon 06 Mon 13 小弟1 小弟2 小弟3 小弟4 小弟5 小弟6 现有任务 等待洗杯机

可以看出,小弟4和小弟6都没有使用洗杯机,而是佛等残渣自己挥发


到这里,基本can tell at a glance——
要是把两个图合在一起,最后那个时间点不就是要求的东西了吗!!
那么把等待洗杯机和等待咖啡机在哪个时间片黏在一起呢?

简单分析如下:
图一的时间从0开始即有意义,因为先做咖啡毋庸置疑;
图二等待洗杯机一定要有人喝完咖啡才有意义,所以时间从第一个人喝完咖啡开始
回到上图就是图一中的小弟1结束时刻和下图的小弟1的开始时刻拼在一起,就得到完整的一张图( 虽然还没结束,但可以开心一下
呵,找到了。

那么之后的选择和调整,就参见之前的算法说明,就可以知道黏在一起的时候,同一个小弟的两个框之间距离是多少(wait的计算),以及如何在佛等干和排队洗杯机之间做出抉择,OVER.

代码演示

public class MinTime {

	public static void main(String[] args) throws IOException{
		int count, n, m, x, y;
		int[] t;
		Scanner sc = new Scanner(System.in);
		count = sc.nextInt();
		MinTime mt = new HelloWorld(); 
		//因为此时调用main方法的时候,main是静态方法,随着类型的加载初始化,但是如果需要使用内部类Coffee对象,需要用外部类的对象
		Coffee s ;//内部类对象
		for(int i = 0; i<count; i++) {
			n = sc.nextInt();
			m = sc.nextInt();
			x = sc.nextInt();
			y = sc.nextInt();
			t = new int[m];
			for(int j= 0; j<m; j++)
				t[j] = sc.nextInt();
			s = mt.new Coffee(n, m, x, y, t); //用外部类对象初始化内部类对象
			System.out.println(s.totalTime()); 
		}
	}
		
		
	
	class Coffee{
		int n, m, x, y, wStatus; //wStatus对应目前使用了洗杯机的总人数
		int[] cTime, cStatus, bTime, eTime; //分别是每个咖啡机煮咖啡的时间,每个咖啡机的使用人数,每个人的开始时间(鸡肋),每个人的结束时间
		
		Coffee(int n, int m, int x, int y, int[] t){ //初始化所有变量
			this.n = n;
			this.m = m;
			this.x = x;
			this.y = y;
			cTime = new int[m];
			for(int k=0; k<m; k++)
				cTime[k] = t[k];
			cStatus = new int[m];
			bTime = new int[n];
			eTime = new int[n];
			wStatus = 0;
		}
		
		void choose1(int i){ //选择咖啡机
			int minCoffee = 0; //假设0号咖啡机耗时最短
			int min = cTime[0] * (cStatus[0] + 1);//如果我选0号咖啡机,直到煮咖啡结束的时间花费
			for(int j=1; j<m; j++) {
				if(cTime[j] * (cStatus[j] + 1) < min) { //更新最短时间咖啡机选择(等待时间+使用时间)
					min = cTime[j] * (cStatus[j] + 1);
					minCoffee = j;
				}
				
			}
			bTime[i] = cTime[minCoffee] * cStatus[minCoffee]; //开始时间
			eTime[i] = bTime[i] + cTime[minCoffee]; //结束时间
			cStatus[minCoffee]++; //使用咖啡机人数++
		}
		
		void choose2(int i) {
			//从0开始计数,目前需要等待的时间
			int wait = wStatus*x + eTime[0] - eTime[i] < 0 ? 0: wStatus*x + eTime[0] - eTime[i];
			
			if(wait+x<y) {//如果等待时间加上洗杯子时间比自己挥发块
				wStatus++; //洗杯机使用人数++
				eTime[i]+=x;
			}
			else
				eTime[i]+=y;
		}
		
		int totalTime() { //最短时间计算
			for(int i=0; i<n; i++) {
				choose1(i);
			}
			for(int i=0; i<n; i++) {
				choose2(i);
			}
			//Arrays.sort(eTime); 最后那个人一定是最后完成的
			//其实上面的choose的执行可以每个人先后执行choose1 choose2
			return eTime[n-1];
		}
	}
	
}

写在最后

  • 个人认为此题的核心就是时间线的记录,当时完全没有想清楚怎么记录时间线,然后晚上回去继续想了一下就大概确定了这个思路,根据相对等待时间来决定绝对时间
  • 另一点是咖啡机和洗杯机两者的时间怎么重合起来,重点要想清楚洗杯机的时间要从第一个人完成咖啡制作之后(第一个人一定选择最短工作时间的咖啡机)才生效,等待时间的比较也是一个点
  • 如果都是顺序遍历每一个人的话,最后的时间不用排序,最后一个人一定是最后一个完成的,因此只用拿最后一个人的endTime即可
  • 最开始看到这个问题的时候,会想到os的资源抢占,排队等待之类,但个人实在不知道如何用多线程来实现,于是想了这样一个静态的办法
  • 有错误欢迎指正讨论
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值