LeetCode 1386. 安排电影院座位
题目描述



题意分析
电影院每排十个座位,四人小组必须坐在同一排,并只能使用
2—5、4—7、6—9这三个允许的座位块。一个块里只要有预订座位就不能使用,每个座位最多分配给一组。求所有排合计最多可以安排多少组。不同排之间互不影响,排数可达
10^9,但预订记录最多10^4条,因此不能逐排建立座位数组或逐排扫描。
解法:位掩码统计座位
核心思路
[!blue]
一排最多安排两组,只有左块
2—5与右块6—9可以同时使用;中块4—7与它们都有重叠。所以每排最优安排可以直接分类:左右块都空闲就放两组,只空闲一个就放一组,两边都不可用时再尝试中块,仍不可用则放零组。这个选择不会漏掉更好安排。只要选中中块,本排就最多一组,不可能再叠加左右块;因此任一侧块可用时,选择它已经不比选择中块差。只有左右都被挡住时,中块才可能补回一组。
用一个整数的不同二进制位表示一排座位是否已预订,座位
seat对应第seat - 1位。将三个候选块也写成掩码,mask & blockMask == 0就表示该块没有任何被占座位。同一排的多条记录通过按位或累积,不能相互覆盖。座位一和十不属于任何候选块,因此这两个位置的预订可以忽略。哈希表只记录二到九号座位受影响的排;其他排无论是否预订了一号或十号座位,都仍能安排左右两组。
先用“总排数减受影响排数”乘二计入默认贡献,再逐个处理哈希表里的受影响排。这样工作量只与预订记录有关,各排答案独立相加就是全局最大值。
解题步骤
- 准备左右和中间三个候选块的掩码。
- 扫描预订记录,只把二到九号座位按位或到对应排的占用掩码。
- 对未记录的排,每排先计入两组。
- 对受影响排,分别检查左块和右块,空闲就各加一组。
- 仅当左右都不可用时检查中块,若空闲再补一组,最后返回总数。
代码实现
class Solution {
public int maxNumberOfFamilies(int n, int[][] reservedSeats) {
// 位号为座位号减一,三个掩码对应题目允许的三个座位块。
int leftMask = 0b0000011110;
int midMask = 0b0001111000;
int rightMask = 0b0111100000;
Map<Integer, Integer> rowMask = new HashMap<>();
for (int[] r : reservedSeats) {
int row = r[0];
int seat = r[1];
// 两端座位不影响任何候选块,可以不记录。
if (seat >= 2 && seat <= 9) {
rowMask.put(row, rowMask.getOrDefault(row, 0) | (1 << (seat - 1)));
}
}
// 未受影响的排统一贡献两组,不逐排遍历。
int res = (n - rowMask.size()) * 2;
for (int mask : rowMask.values()) {
boolean left = (mask & leftMask) == 0;
boolean right = (mask & rightMask) == 0;
if (left) {
res++;
}
if (right) {
res++;
}
// 中块与左右块都重叠,只能在左右都不可用时补一组。
if (!left && !right && (mask & midMask) == 0) {
res++;
}
}
return res;
}
}
func maxNumberOfFamilies(n int, reservedSeats [][]int) int {
// 位号为座位号减一,三个掩码对应题目允许的三个座位块。
leftMask := 0b0000011110
midMask := 0b0001111000
rightMask := 0b0111100000
rowMask := make(map[int]int)
for _, r := range reservedSeats {
row := r[0]
seat := r[1]
// 两端座位不影响任何候选块,可以不记录。
if seat >= 2 && seat <= 9 {
rowMask[row] |= 1 << (seat - 1)
}
}
// 未受影响的排统一贡献两组,不逐排遍历。
res := (n - len(rowMask)) * 2
for _, mask := range rowMask {
left := (mask & leftMask) == 0
right := (mask & rightMask) == 0
if left {
res++
}
if right {
res++
}
// 中块与左右块都重叠,只能在左右都不可用时补一组。
if !left && !right && (mask&midMask) == 0 {
res++
}
}
return res
}
复杂度分析
设预订记录数为 $m$,其中影响候选座位块的不同排数为 $r$。
- 时间复杂度:期望 $O(m)$,构建哈希表后每个受影响排只检查三个固定掩码,且 $r\le m$。
- 辅助空间复杂度:$O(r+1)$,保存受影响排的占用状态,不随总排数线性增长。
关键点总结
[!green]
- 左右块能同时安排,中块只提供一种替代的一组方案。
- 位掩码同时记录一排全部预订,按位与判断候选块是否空闲。
- 未受影响的排统一计两组,只遍历少量受影响排。
易错点总结
[!yellow]
- 忽略中块会漏掉两侧受阻而中间空闲的一组方案。
- 把中块与左块或右块同时计数,会重复使用共享座位。
- 默认贡献要减去不同的受影响排数,不能减去预订记录条数。
- 更新占用掩码必须使用按位或,直接赋值会丢失同排之前的预订。
- 座位号与位号相差一,预订位移和块掩码必须保持同一编号规则。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 1349. 参加考试的最大学生数 | 困难 | 同样可用位掩码表示一行座位,原题还限制前后排,本题各行独立且只检查少数合法四人块。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!