leetcode
leetcode 1251 ~ 1300
安排电影院座位

安排电影院座位

难度:

标签:

题目描述

如上图所示,电影院的观影厅中有 n 行座位,行编号从 1 到 n ,且每一行内总共有 10 个座位,列编号从 1 到 10 。

给你数组 reservedSeats ,包含所有已经被预约了的座位。比如说,reservedSeats[i]=[3,8] ,它表示第 3 行第 8 个座位被预约了。

请你返回 最多能安排多少个 4 人家庭 。4 人家庭要占据 同一行内连续 的 4 个座位。隔着过道的座位(比方说 [3,3] 和 [3,4])不是连续的座位,但是如果你可以将 4 人家庭拆成过道两边各坐 2 人,这样子是允许的。

 

示例 1:

输入:n = 3, reservedSeats = [[1,2],[1,3],[1,8],[2,6],[3,1],[3,10]]
输出:4
解释:上图所示是最优的安排方案,总共可以安排 4 个家庭。蓝色的叉表示被预约的座位,橙色的连续座位表示一个 4 人家庭。

示例 2:

输入:n = 2, reservedSeats = [[2,1],[1,8],[2,6]]
输出:2

示例 3:

输入:n = 4, reservedSeats = [[4,3],[1,4],[4,6],[1,7]]
输出:4

 

提示:

  • 1 <= n <= 10^9
  • 1 <= reservedSeats.length <= min(10*n, 10^4)
  • reservedSeats[i].length == 2
  • 1 <= reservedSeats[i][0] <= n
  • 1 <= reservedSeats[i][1] <= 10
  • 所有 reservedSeats[i] 都是互不相同的。

代码结果

运行时间: 66 ms, 内存: 19.6 MB


/*
 * 思路:
 * 1. 使用 Java Stream API 处理 reservedSeats 数组,将其转为一个 Map。
 * 2. 对于每一行,检查是否可以安排 4 人家庭在三个可能的位置:
 *    - 座位 2 到 5
 *    - 座位 4 到 7
 *    - 座位 6 到 9
 * 3. 根据每一行可以安排的 4 人家庭数量进行累加。
 */

import java.util.*;
import java.util.stream.*;

public class CinemaSeatAllocationStream {
    public int maxNumberOfFamilies(int n, int[][] reservedSeats) {
        Map<Integer, Set<Integer>> reservedMap = Arrays.stream(reservedSeats)
            .collect(Collectors.groupingBy(seat -> seat[0], Collectors.mapping(seat -> seat[1], Collectors.toSet())));

        int maxFamilies = reservedMap.values().stream().mapToInt(reserved -> {
            boolean canPlaceLeft = !reserved.contains(2) && !reserved.contains(3) && !reserved.contains(4) && !reserved.contains(5);
            boolean canPlaceMiddle = !reserved.contains(4) && !reserved.contains(5) && !reserved.contains(6) && !reserved.contains(7);
            boolean canPlaceRight = !reserved.contains(6) && !reserved.contains(7) && !reserved.contains(8) && !reserved.contains(9);
            int count = 0;
            if (canPlaceLeft) count++;
            if (canPlaceRight) count++;
            else if (canPlaceMiddle) count++;
            return count;
        }).sum();

        return maxFamilies + 2 * (n - reservedMap.size());
    }
}

解释

方法:

此题解使用了位操作和哈希表来有效地计算最多可以安排的4人家庭数量。首先,使用哈希表('tab')记录那些有至少一个被预订的座位的行,键是行号,值是一个整数,对应于行中被预订的座位的位标记。仅关注第2到第9列的座位,因为第1列和第10列的座位无法容纳一个完整的4人家庭。使用位操作将座位映射到一个8位的整数中。然后,对于那些没有任何预订记录的行,因为每行可以容纳两个4人家庭,所以简单地将这些行的家庭数量乘以2加到结果中。对于有预订记录的行,检查通过位掩码判断是否可以安排家庭。如果能在左侧(2-5座)、右侧(6-9座)或中间(4-7座)安排,则相应地增加计数。如果一行能同时在左侧和右侧安排家庭,则可以安排两个家庭,否则检查是否可以在中间安排一个家庭。

时间复杂度:

O(m)

空间复杂度:

O(m)

代码细节讲解

🦆
在位操作中,为什么选择将第2到第9列的座位映射到一个8位的整数,而不包含第1列和第10列?
因为一个4人家庭需要连续4个座位,所以第1列和第10列的座位实际上无法独立容纳一个完整的4人家庭。只有第2列到第9列的座位才能可能组成两个独立的4人家庭区域(例如2-5列和6-9列),因此只需要关注这些座位。使用8位整数可以精确地表示这些座位的占用情况,使得位操作可以高效地应用于检查和计算可用的家庭座位区域。
🦆
位掩码定义中的`l`, `r`, `m`分别表示哪些座位?具体的位值`0b00001111`, `0b11110000`, `0b11000011`是如何确定的?
`l`代表左侧的座位区域,涵盖2-5列,对应位掩码是`0b00001111`。`r`代表右侧的座位区域,涵盖6-9列,对应位掩码是`0b11110000`。`m`代表中间的座位区域,涵盖4-7列,对应位掩码是`0b11000011`。这些位掩码是根据家庭座位需要连续4个空位来定义的,确保各个区域可以容纳一个4人家庭,而具体的位值则是直接反映了每个区域座位的位置。
🦆
为什么在处理完全没有被预订的行时,可以直接假定每行可以安排两个家庭?
如果一行没有任何座位被预订,那么第2列到第9列的所有座位都是空的。这允许在2-5列和6-9列各自安排一个4人家庭,共两个家庭。这种情况下,每行的空座位充足,没有任何障碍阻止两个家庭的安排。因此,在没有预订的情况下,可以最大化利用座位,每行安排两个家庭。
🦆
算法中如何处理一行中已经有部分座位被预订的情况?特别是在检查能否安排家庭时使用的逻辑是什么?
对于已有部分座位被预订的行,算法首先将这些座位的占用情况表示为一个8位的整数。然后使用位掩码`l`, `r`, `m`来检查是否能在左侧、右侧或中间安排一个家庭。使用位或操作(`|`)和比较操作来确定是否所有需要的座位都是空的。例如,如果`(tab[row] | l) == l`成立,说明左侧4个座位(2-5列)都是空的,可以安排一个家庭。同理,检查右侧和中间区域。如果一行中左侧和右侧都可以安排家庭,则总计可以安排两个家庭,否则检查中间区域是否可以安排一个家庭。

相关问题