Medium
题目描述
一个影院有 n 排座位,编号从 1 到 n,每排有 10 个座位,编号从 1 到 10。
给你数组 reservedSeats,包含所有已经被预订的座位,例如 reservedSeats[i] = [3,8] 表示第 3 排第 8 号座位被预订了。
请你返回可以安排的四人家庭的最大数目,一个四人家庭占据同一排中四个相邻的座位。跨过过道的座位(比如 [3,3] 和 [3,4])不被认为是相邻的,但是有一种特殊情况,如果一个四人家庭可以被过道分割成两部分,那么过道可以分割一个四人家庭的中间,也就是每边坐两个人。
示例 1:
输入:n = 3, reservedSeats = [[1,2],[1,3],[1,8],[2,6],[3,1],[3,10]]
输出: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] 都是互不相同的。
解题思路
解题思路
这道题的关键是理解座位布局和四人家庭的可能安排方式。每排有10个座位,分为三个区域:1-2(左侧),3-4-5-6-7-8(中间),9-10(右侧)。
可能的四人家庭安排方式:
- 座位 2-3-4-5(左区域)
- 座位 4-5-6-7(左中区域)
- 座位 6-7-8-9(右中区域)
贪心策略:
- 对于没有预订座位的行,每行可以安排2个家庭(方式1+3或者方式2)
- 对于有预订座位的行,需要检查哪些安排方式仍然可行
- 优先选择不冲突的组合,最多每行2个家庭
算法步骤:
- 统计每行的预订座位情况
- 对于有预订的行,使用位操作或集合检查可行的安排
- 对于未出现在预订列表中的行,每行贡献2个家庭
- 求和得到最终结果
时间复杂度优化: 由于n可能很大(10^9),但预订座位最多10^4个,我们只需要处理有预订的行,其他行都按每行2个家庭计算。
代码实现
class Solution {
public:
int maxNumberOfFamilies(int n, vector<vector<int>>& reservedSeats) {
unordered_map<int, int> rowMask;
// 构建每行的预订座位掩码
for (auto& seat : reservedSeats) {
int row = seat[0];
int col = seat[1];
if (col >= 2 && col <= 9) { // 只关心2-9的座位
rowMask[row] |= (1 << (col - 2));
}
}
int result = 0;
// 处理有预订座位的行
for (auto& [row, mask] : rowMask) {
int families = 0;
// 检查左区域 (2,3,4,5) -> 位置 (0,1,2,3)
if ((mask & 15) == 0) { // 15 = 1111
families++;
mask |= 15; // 标记这些位置已使用
}
// 检查右区域 (6,7,8,9) -> 位置 (4,5,6,7)
if ((mask & 240) == 0) { // 240 = 11110000
families++;
}
// 如果左右都不能放,尝试中间区域 (4,5,6,7) -> 位置 (2,3,4,5)
if (families == 0 && (mask & 60) == 0) { // 60 = 111100
families = 1;
}
result += families;
}
// 未出现在预订列表中的行,每行2个家庭
result += (n - rowMask.size()) * 2;
return result;
}
};
class Solution:
def maxNumberOfFamilies(self, n: int, reservedSeats: List[List[int]]) -> int:
row_mask = {}
# 构建每行的预订座位掩码
for row, col in reservedSeats:
if 2 <= col <= 9: # 只关心2-9的座位
if row not in row_mask:
row_mask[row] = 0
row_mask[row] |= (1 << (col - 2))
result = 0
# 处理有预订座位的行
for row, mask in row_mask.items():
families = 0
# 检查左区域 (2,3,4,5) -> 位置 (0,1,2,3)
if (mask & 15) == 0: # 15 = 1111
families += 1
mask |= 15 # 标记这些位置已使用
# 检查右区域 (6,7,8,9) -> 位置 (4,5,6,7)
if (mask & 240) == 0: # 240 = 11110000
families += 1
# 如果左右都不能放,尝试中间区域 (4,5,6,7) -> 位置 (2,3,4,5)
if families == 0 and (mask & 60) == 0: # 60 = 111100
families = 1
result += families
# 未出现在预订列表中的行,每行2个家庭
result += (n - len(row_mask)) * 2
return result
public class Solution {
public int MaxNumberOfFamilies(int n, int[][] reservedSeats) {
Dictionary<int, int> rowMask = new Dictionary<int, int>();
// 构建每行的预订座位掩码
foreach (var seat in reservedSeats) {
int row = seat[0];
int col = seat[1];
if (col >= 2 && col <= 9) { // 只关心2-9的座位
if (!rowMask.ContainsKey(row)) {
rowMask[row] = 0;
}
rowMask[row] |= (1 << (col - 2));
}
}
int result = 0;
// 处理有预订座位的行
foreach (var kvp in rowMask) {
int mask = kvp.Value;
int families = 0;
// 检查左区域 (2,3,4,5) -> 位置 (0,1,2,3)
if ((mask & 15) == 0) { // 15 = 1111
families++;
mask |= 15; // 标记这些位置已使用
}
// 检查右区域 (6,7,8,9) -> 位置 (4,5,6,7)
if ((mask & 240) == 0) { // 240 = 11110000
families++;
}
// 如果左右都不能放,尝试中间区域 (4,5,6,7) -> 位置 (2,3,4,5)
if (families == 0 && (mask & 60) == 0) { // 60 = 111100
families = 1;
}
result += families;
}
// 未出现在预订列表中的行,每行2个家庭
result += (n - rowMask.Count) * 2;
return result;
}
}
var maxNumberOfFamilies = function(n, reservedSeats) {
const reservedByRow = new Map();
for (const [row, seat] of reservedSeats) {
if (!reservedByRow.has(row)) {
reservedByRow.set(row, new Set());
}
reservedByRow.get(row).add(seat);
}
let result = 0;
// Count families in rows without any reservations
result += (n - reservedByRow.size) * 2;
// Count families in rows with reservations
for (const [row, reserved] of reservedByRow) {
let families = 0;
// Check if seats 2,3,4,5 are available (left group)
if (!reserved.has(2) && !reserved.has(3) && !reserved.has(4) && !reserved.has(5)) {
families++;
}
// Check if seats 6,7,8,9 are available (right group)
if (!reserved.has(6) && !reserved.has(7) && !reserved.has(8) && !reserved.has(9)) {
families++;
}
// If no families found yet, check middle group 4,5,6,7
if (families === 0 && !reserved.has(4) && !reserved.has(5) && !reserved.has(6) && !reserved.has(7)) {
families = 1;
}
result += families;
}
return result;
};
复杂度分析
| 指标 | 复杂度 |
|---|---|
| 时间复杂度 | O(m),其中 m 是 reservedSeats 的长度 |
| 空间复杂度 | O(k),其中 k 是有预订座位的行数,最多为 min(m, 10^4) |