LeetCode探索(60):1353-最多可以参加的会议数目

281 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第17天,点击查看活动详情

题目

给你一个数组 events,其中 events[i] = [startDayi, endDayi] ,表示会议 i 开始于 startDayi ,结束于 endDayi

你可以在满足 startDayi <= d <= endDayi 中的任意一天 d 参加会议 i 。注意,一天只能参加一个会议。

请你返回你可以参加的 最大 会议数目。

示例 1:

img

输入:events = [[1,2],[2,3],[3,4]]
输出:3
解释:你可以参加所有的三个会议。
安排会议的一种方案如上图。
第 1 天参加第一个会议。
第 2 天参加第二个会议。
第 3 天参加第三个会议。

示例 2:

输入:events= [[1,2],[2,3],[3,4],[1,2]]
输出:4

提示:

  • 1 <= events.length <= 10^5
  • events[i].length == 2
  • 1 <= startDayi <= endDayi <= 10^5

思考

本题难度中等。

前天刚做过一道类似的题目用最少数量的箭引爆气球,所以最开始想着套用前面的解法去解题,但是,尝试了几次,测试用例中总有不通过的例子。仔细一分析,两道题其实不一样!本题中天数是整数,且天数会被某一个会议占用,而"用最少数量的箭引爆气球"中没有这个要求... 那么,只能换种解法了。

借鉴了其他人的解法,比如贪心算法、并查集等,这里尝试用并查集解题。并查集 是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。初始化是把每一个list[i]指向自己,没有合并的元素,然后实现查找和合并方法。

我们首先对会议数组进行排序,对于结束时间晚的排在后面,结束时间相同则开始时间早的排在前面。排序方法是借用sort()方法:events.sort((a, b) => a[1] - b[1] || a[0] - b[0]);

var events = [[3, 3], [1, 3], [2, 3], [3, 4], [3, 4]];
events.sort((a, b) => (a[1] === b[1] ? a[0] - b[0] : a[1] - b[1]));
console.log(events); // [[1, 3], [2, 3], [3, 3], [3, 4], [3, 4]]

总的来说,并查集理解得还不是很透彻,也不是很熟练,后面有时间再理一理思路。

下面是具体代码。

解答

方法一:贪心

// 并查集
class UnionFind {
  constructor(n) {
    this.list = Array(n);
    for (let i = 0; i < n; i++) {
      this.list[i] = i;
    }
  }
  // 查找
  find(x) {
    // 如果当前元素为根节点,返回
    if (this.list[x] === x) return x;
    // 路径压缩
    this.list[x] = this.find(this.list[x]);
    return this.list[x];
  }
  // 合并
  merge(a, b) {
    const rootA = this.find(a), rootB = this.find(b);
    this.list[rootA] = rootB;
  }
}
/**
 * @param {number[][]} events
 * @return {number}
 */
const maxEvents = function (events) {
  // 对会议排序,结束时间晚的排在后面,结束时间相同,开始时间早的排在前面
  events.sort((a, b) => a[1] - b[1] || a[0] - b[0]);
  // 初始化并查集,长度为最晚天数+1
  const uf = new UnionFind(events[events.length - 1][1] + 1);
  // 初始化结果值
  let res = 0;
  // 遍历排序后的会议列表
  for (let i = 0; i < events.length; i++) {
    // 获取会议开始和结束天数、获取开始时间在并查集中对应的未使用天数
    const [a, b] = events[i], day = uf.find(a);
    // 如果该天数可以参加当前会议,则可参加会议+1,更新并查集当前天数为下一天
    if (day <= b) {
      uf.merge(day, day + 1);
      res++;
    }
  }
  return res;
};

// 执行用时:180 ms, 在所有 JavaScript 提交中击败了97.83%的用户
// 内存消耗:67.8 MB, 在所有 JavaScript 提交中击败了56.52%的用户
// 通过测试用例:44 / 44

参考