描述
有n个活动即将举办,每个活动都有开始时间与活动的结束时间,第i个活动的开始时间是 starti,第i个活动的结束时间是 endi,举办某个活动就需要为该活动准备一个活动主持人。
一位活动主持人在同一时间只能参与一个活动。并且活动主持人需要全程参与活动,换句话说,一个主持人参与了第i个活动,那么该主持人在 (starti,endi)这个时间段不能参与其他任何活动。求为了成功举办这n个活动,最少需要多少名主持人。
数据范围:
方法一
思路
- 活动调度,模拟主持人分配
- 不知道读者了不了解活动调度问题,活动调度是黑皮书《算法导论》贪心算法章节的第一个栗子,给你一组活动的起始时间和结束时间,选出一个最大的互不冲突的活动集。
- 而本题可以说是活动调度问题的衍生,所以我们可以将主持人调度转换成活动调度,将所给活动集合划分成若干个活动集合,每个活动集合都是当前的最大的互不冲突的活动集,那么活动集合的数量即为题目所要求的最少主持人数量。
- 下面介绍一下活动调度算法:
- 具体的推导过程我就不细说了,感兴趣的可以去看黑皮书,这里用到了贪心思想,对于任意非空子问题Sk,am是Sk中结束时间最早的活动,则am在Sk的某个最大互不冲突的活动集中,也就是说对于所给的活动集合,只需在时间上不发生冲突的前提下,尽量选择结束时间早的活动,这样所选出的活动集即为最大互不冲突活动集。
- 而为了求最少主持人数,只需要在找出一个最大互不冲突活动集后,继续对剩下的活动集合进行活动调度,找出第二个最大兼容活动集,依次类推直至所有活动均被分配。
- 代码如下:
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * 计算成功举办活动需要多少名主持人 * @param n int整型 有n个活动 * @param startEnd int整型二维数组 startEnd[i][0]用于表示第i个活动的开始时间,startEnd[i][1]表示第i个活动的结束时间 * @return int整型 * 通过活动调度解决问题 */ public int minmumNumberOfHost (int n, int[][] startEnd) { if (n == 0){ return 0; } // 对活动按照开始时间进行升序排序 Arrays.sort(startEnd,Comparator.comparingInt(o -> o[0])); // 辅助判断活动是否已经分配主持人 boolean[] solved = new boolean[startEnd.length]; int res = 0; // 模拟主持人分配 while(activityOrder(startEnd,solved)){ ++res; } return res; } // 活动调度算法, private boolean activityOrder(int[][] startEnd,boolean[] solved){ // 初始活动结束时间 int end = Integer.MIN_VALUE; // 判断所有活动是否都已经分配 boolean flag = false; int i = 0; // 活动调度 while(i < startEnd.length){ if (!solved[i] && startEnd[i][0] >= end){ solved[i] = true; flag = true; end = startEnd[i][1]; } ++i; } return flag; } }
- 时间复杂度:,最坏情况是所有的活动都冲突了,总共要分配n个主持人,而每次活动调度需要n;
- 空间复杂度:,一维数组辅助运算。
方法二
思路
- 贪心,最小堆,排序
- 开始结束时间相冲突的活动一定是需要不同的主持人主持的,故我们可以模拟活动的进行,同时进行的活动的最大数量就是需要的最少的主持人人数。
- 由于活动需要按照时间线运行,所以需要先对所给的活动集按照开始时间进行升序排序。
- 依据活动的结束时间创建最小堆p,p便是活动发生的场所,从左往右遍历升序活动集。
- 堆空,则活动直接入栈,开始活动;
- 堆非空,比较当前活动的开始时间与堆顶的活动的结束时间,若大于等于,则当前活动开始时,堆顶活动已经结束,所以弹出堆顶元素,继续与当前活动进行比较,直至堆空,或者时当前活动的开始时间小于堆顶活动结束时间。
- 遍历结束,所需的最少主持人数即为最小堆中同时进行的最大活动数。
- 参考下图示例:
开始时间升序活动集为:[[1,2][1,3][2,4],[2,5],[3,6],[4,5],[5,6]];
- 代码如下:
import java.util.*; public class Solution { // 活动类 class Activity{ int start; int end; public Activity(int start,int end){ this.start = start; this.end = end; } } /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * 计算成功举办活动需要多少名主持人 * @param n int整型 有n个活动 * @param startEnd int整型二维数组 startEnd[i][0]用于表示第i个活动的开始时间,startEnd[i][1]表示第i个活动的结束时间 * @return int整型 * 通过活动调度解决问题 */ public int minmumNumberOfHost (int n, int[][] startEnd) { if (n == 0){ return 0; } // 按活动开始时间升序排序 Arrays.sort(startEnd,Comparator.comparingInt(o -> o[0])); int count = 0; // 当前正在进行的活动,活动结束时间的最小堆 PriorityQueue<Activity> parallelActivities = new PriorityQueue<>((o1,o2) -> o1.end - o2.end); for (int i = 0;i < n;++i){ Activity activity = new Activity(startEnd[i][0],startEnd[i][1]); // 队空,直接入队 if (parallelActivities.isEmpty()){ parallelActivities.offer(activity); } // 队非空 else { while(!parallelActivities.isEmpty()){ Activity a = parallelActivities.peek(); // 待执行活动在队首元素结束之后开始,弹出队首元素 if (activity.start >= a.end){ parallelActivities.poll(); }else{ break; } } parallelActivities.offer(activity); } // 取最大值 count = Math.max(parallelActivities.size(),count); } return count; } }
- 时间复杂度:,排序的时间复杂度为,之后的主持人调度是;
- 空间复杂度:,使用最小堆存储活动,最坏情况下是。