描述
有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;
}
}- 时间复杂度:
,排序的时间复杂度为
,之后的主持人调度是
;
- 空间复杂度:
,使用最小堆存储活动,最坏情况下是
。

京公网安备 11010502036488号