华为OD机考:停车场收入统计算法实现与优化 1. 停车场收入统计问题解析停车场收费系统是每个开发者都可能遇到的经典业务场景今天我们来拆解华为OD机考中的这道停车场收入统计题目。这道题看似简单但包含了时间计算、条件判断、数据结构应用等多个编程核心知识点非常考验开发者的基本功和业务逻辑处理能力。2. 问题需求与规则详解2.1 基础收费规则首先我们需要明确题目给出的所有收费规则基础计费标准每半小时收费1元不足半小时按半小时计算免费时段停车时间小于半小时不收费每日封顶单日单辆车最高收费15元午休时段11:30-13:30不计入停车时间包月车辆已购买包月服务的车辆不参与当日收入统计2.2 输入输出规范输入分为两部分包月车辆信息第一行包月车辆数量N后续N行每行一个包月车牌号当日出入记录每行格式时:分 车牌号 enter/leave示例08:30 ABC123 enter输出要求当日停车场总收入整数2.3 边界条件与假设题目给出了几个重要假设所有车辆都是当天进当天出每日出入记录少于10000条包月车辆少于10000辆3. 解题思路与算法设计3.1 核心处理流程解决这个问题的关键步骤可以分为数据预处理读取并存储包月车辆列表按时间顺序整理出入记录记录处理为每辆车维护进出时间对过滤包月车辆费用计算计算有效停车时间扣除午休时段应用收费规则计算单辆车费用累加所有车辆费用3.2 数据结构选择考虑到车牌号需要快速查找判断是否为包月车辆我们使用HashSet存储包月车辆SetString monthlyMembers new HashSet();对于车辆进出记录可以使用Map来关联车牌和进出时间MapString, ListParkRecord vehicleRecords new HashMap();3.3 时间处理策略时间计算是本问题的核心难点需要特别注意将时间统一转换为分钟数便于计算处理跨午休时段的停车情况计算实际收费时长时间转换方法示例private static int timeToMinutes(String time) { String[] parts time.split(:); return Integer.parseInt(parts[0]) * 60 Integer.parseInt(parts[1]); }4. 完整Java实现方案4.1 基础数据结构定义首先定义停车记录类class ParkRecord { String plate; int time; // in minutes boolean isEnter; public ParkRecord(String plate, int time, boolean isEnter) { this.plate plate; this.time time; this.isEnter isEnter; } }4.2 主处理逻辑public class ParkingIncomeCalculator { public static void main(String[] args) { Scanner scanner new Scanner(System.in); // 读取包月车辆 int monthlyCount Integer.parseInt(scanner.nextLine()); SetString monthlyMembers new HashSet(); for (int i 0; i monthlyCount; i) { monthlyMembers.add(scanner.nextLine().trim()); } // 读取出入记录 ListParkRecord records new ArrayList(); while (scanner.hasNextLine()) { String line scanner.nextLine().trim(); if (line.isEmpty()) continue; String[] parts line.split( ); String timeStr parts[0]; String plate parts[1]; String action parts[2]; int time timeToMinutes(timeStr); records.add(new ParkRecord(plate, time, action.equals(enter))); } // 按时间排序 records.sort(Comparator.comparingInt(r - r.time)); // 处理每辆车的记录 MapString, ListParkRecord vehicleRecords new HashMap(); for (ParkRecord record : records) { if (monthlyMembers.contains(record.plate)) continue; vehicleRecords.computeIfAbsent(record.plate, k - new ArrayList()) .add(record); } // 计算总收入 int totalIncome 0; for (Map.EntryString, ListParkRecord entry : vehicleRecords.entrySet()) { ListParkRecord plateRecords entry.getValue(); // 确保进出记录配对 if (plateRecords.size() % 2 ! 0) continue; // 计算该车总停车时间 int totalParkMinutes 0; for (int i 0; i plateRecords.size(); i 2) { ParkRecord enter plateRecords.get(i); ParkRecord leave plateRecords.get(i 1); totalParkMinutes calculateEffectiveParkTime(enter.time, leave.time); } // 计算该车费用 int fee calculateParkingFee(totalParkMinutes); totalIncome fee; } System.out.println(totalIncome); } // 其他工具方法... }4.3 关键工具方法实现计算有效停车时间扣除午休private static int calculateEffectiveParkTime(int enterTime, int leaveTime) { int lunchStart 11 * 60 30; // 11:30 int lunchEnd 13 * 60 30; // 13:30 // 完全在午休前或后 if (leaveTime lunchStart || enterTime lunchEnd) { return leaveTime - enterTime; } // 完全包含午休时段 if (enterTime lunchStart leaveTime lunchEnd) { return (leaveTime - enterTime) - (lunchEnd - lunchStart); } // 部分重叠午休时段 if (enterTime lunchStart) { return lunchStart - enterTime; } else { return leaveTime - lunchEnd; } }计算停车费用private static int calculateParkingFee(int totalMinutes) { if (totalMinutes 30) return 0; // 计算半小时数向上取整 int halfHours (totalMinutes 29) / 30; int fee halfHours * 1; // 应用封顶 return Math.min(fee, 15); }5. 边界情况与测试用例5.1 典型测试场景基本案例输入1 VIP001 08:00 CAR001 enter 12:00 CAR001 leave输出7扣除午休2小时实际停车1.5小时3个半小时跨午休停车输入0 11:00 CAR002 enter 14:00 CAR002 leave输出3实际停车时间3小时扣除午休2小时包月车辆输入1 CAR003 09:00 CAR003 enter 17:00 CAR003 leave输出05.2 特殊边界情况刚好半小时输入0 10:00 CAR004 enter 10:30 CAR004 leave输出1不足半小时输入0 10:00 CAR005 enter 10:29 CAR005 leave输出0多次进出输入0 08:00 CAR006 enter 10:00 CAR006 leave 13:31 CAR006 enter 14:31 CAR006 leave输出5上午2小时4元下午1小时2元合计6元但封顶156. 性能优化与代码改进6.1 时间复杂度分析当前实现的时间复杂度主要来自记录排序O(n log n)记录处理O(n)费用计算O(m)m为车辆数总体复杂度为O(n log n)对于题目给定的n10000完全足够。6.2 内存优化建议如果处理超大规模数据可以考虑流式处理记录不全部存储在内存使用更紧凑的数据结构存储时间信息6.3 代码可读性改进使用枚举代替布尔值表示进出状态enum ParkAction { ENTER, LEAVE }添加更多注释说明复杂逻辑提取更多工具方法保持主逻辑清晰7. 常见问题与调试技巧7.1 典型错误排查时间计算错误确保午休时段正确处理验证时间转换函数是否正确费用计算错误检查半小时向上取整逻辑验证封顶规则应用包月车辆过滤失败检查HashSet的contains方法调用确保车牌号比对时大小写一致7.2 调试建议打印中间结果System.out.println(Processing plate: plate , park time: totalParkMinutes);使用单元测试验证关键方法Test void testCalculateEffectiveParkTime() { assertEquals(60, calculateEffectiveParkTime(10*60, 11*60)); // 完全在午休前 assertEquals(60, calculateEffectiveParkTime(14*60, 15*60)); // 完全在午休后 assertEquals(90, calculateEffectiveParkTime(11*60, 14*60)); // 跨午休 }使用断言检查关键假设assert enter.isEnter !leave.isEnter : Invalid record pair;8. 扩展思考与变体问题8.1 问题变体多日停车如果车辆可能隔夜停放如何修改算法分段计价不同时段不同费率如何处理VIP折扣不同会员等级享受不同折扣8.2 实际业务扩展在实际停车场系统中还需要考虑异常情况处理如重复进出记录数据持久化存储实时统计与报表生成8.3 算法优化方向对于更大规模数据使用并行流处理记录采用更高效的时间区间计算算法实现增量计算支持实时更新这道题目虽然来自机考但很好地反映了实际业务中的数据处理需求。通过这个练习我们不仅掌握了时间处理和条件判断的技巧也学习了如何将业务规则转化为清晰的代码逻辑。在实际开发中类似的场景比比皆是良好的问题分解能力和严谨的边界条件处理是写出健壮代码的关键。