1. 项目背景与问题定义
UVa 12189 "Dinner Hall" 是ACM国际大学生程序设计竞赛(ICPC)中的一道经典算法题目。这道题考察选手对贪心算法(Greedy Algorithm)和时间区间管理(Interval Management)的理解与应用能力。题目场景设定在一个餐厅的就餐高峰期,需要合理安排顾客进出顺序以保证餐厅容量限制。
典型的问题描述是:餐厅有一个最大容纳人数限制(比如100人)。顾客们在不同时间到达和离开,每个顾客的用餐时间段用(到达时间,离开时间)表示。我们需要计算在任意时刻,餐厅内的顾客人数都不超过最大容量的前提下,最多可以接待多少顾客。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心算法思路解析
2.1 事件点处理法
这类时间区间问题通常采用"事件点"(Event Point)的处理方法。具体步骤是:
- 将每个顾客的到达和离开时间转换为事件点
- 给到达事件标记为+1(表示人数增加),离开事件标记为-1
- 将所有事件按时间排序(时间相同则离开事件优先处理)
python复制def max_customers(intervals, capacity):
events = []
for start, end in intervals:
events.append((start, 1)) # 到达事件
events.append((end, -1)) # 离开事件
# 排序规则:时间优先,离开事件优先
events.sort(key=lambda x: (x[0], x[1]))
current = 0
max_customers = 0
for time, delta in events:
current += delta
if current > capacity:
return -1 # 无法满足容量限制
max_customers = max(max_customers, current)
return max_customers
2.2 贪心算法应用
当需要选择哪些顾客可以进入餐厅时(即题目要求最大化接待顾客数),我们需要采用贪心策略:
- 优先选择离开时间早的顾客
- 使用优先队列(堆)来管理当前在餐厅的顾客
- 当新顾客到达时,如果餐厅已满,则比较其离开时间与队列中最晚离开的顾客
python复制import heapq
def max_admitted(intervals, capacity):
intervals.sort() # 按到达时间排序
heap = []
admitted = 0
for start, end in intervals:
# 移除已经离开的顾客
while heap and heap[0] <= start:
heapq.heappop(heap)
if len(heap) < capacity:
heapq.heappush(heap, end)
admitted += 1
else:
# 比较当前顾客与最晚离开的顾客
if end < heap[-1]:
heapq.heappop(heap)
heapq.heappush(heap, end)
return admitted
3. 关键实现细节与优化
3.1 时间处理技巧
在实际编码中,时间点的表示和处理有几个需要注意的细节:
- 时间标准化:将所有时间转换为分钟数(如18:30转为1110)便于比较
- 边界情况:同一时间点的到达和离开事件,必须确保离开事件先处理
- 时间重叠:特别注意完全重叠的时间区间处理
重要提示:在排序比较函数中,必须确保当时间相同时,离开事件(-1)排在到达事件(+1)前面,否则会导致人数统计错误。
3.2 数据结构选择
根据问题变种的不同,可能需要选择不同的数据结构:
- 基本计数问题:使用简单数组存储事件点
- 最大化接待顾客:使用最小堆(优先队列)
- 动态容量变化:可能需要使用线段树或二叉索引树
对于UVa 12189这类题目,通常优先队列是最佳选择,因为它的时间复杂度为O(n log n),空间复杂度为O(n),完全在竞赛要求的范围内。
4. 常见错误与调试技巧
4.1 典型错误案例
-
错误排序:没有正确处理同时发生的到达和离开事件
- 错误表现:人数统计突然异常增加或减少
- 修正方法:确保比较函数中,时间相同时离开事件优先
-
容量检查遗漏:没有在每次人数变化时检查容量限制
- 错误表现:最终结果正确但中间过程可能超限
- 修正方法:在current += delta后立即检查if current > capacity
-
整数溢出:使用32位整数处理大时间范围
- 错误表现:计算结果出现异常负值
- 修正方法:统一使用64位整数(long long in C++)
4.2 测试用例设计
设计测试用例时应考虑以下边界情况:
- 所有顾客同时到达和离开
- 餐厅容量为1的特殊情况
- 时间区间完全包含的情况
- 大规模数据测试(如1e5个顾客)
示例测试用例:
code复制输入:
3 2
1 5
2 4
3 6
输出:
2
解释:餐厅容量为2,最优方案是接受第2和第3个顾客,他们在时间3-4期间同时在餐厅。
5. 算法扩展与变种
5.1 问题变种
-
最小会议室问题:求需要多少个容量为1的"餐厅"才能容纳所有顾客
- 解法:统计最大重叠区间数
-
加权区间调度:每个顾客有不同权重,求最大权重和
- 解法:动态规划+二分查找
-
动态容量:餐厅容量随时间变化
- 解法:需要结合线段树等高级数据结构
5.2 实际应用场景
这类算法在实际中有广泛应用:
- 酒店房间分配
- 课程教室安排
- 服务器资源调度
- 医院床位管理
在工业界面试中,这类问题也经常出现(如LeetCode上的"Meeting Rooms"系列题目)。掌握好基础的事件点处理方法,可以应对大多数时间区间管理问题。
