华为机试模拟题3:停车位题目从暴力解到线性解
华为机试的编程模拟题我前前后后刷了不少。最开始是照着答案抄抄完还是懵后来逼着自己一步一步画状态才慢慢摸到出题人的套路。今天借着“华为机试编程模拟题3”这个题目聊聊模拟题到底要练什么以及我是怎么把一道典型的停车位题目从暴力解优化到线性解的过程。如果你正在准备华为OD机试或者类似公司的在线编程笔试这篇应该能帮你少走一些弯路。为什么单独说“模拟题3”因为模拟题系列里的第三题通常最接近真实机试中的压轴题它不会只考一个孤立的知识点而是会把字符串解析、数组遍历、边界处理、复杂度优化混合在一起。把它吃透比刷十道简单题更有价值。1. 华为机试模拟题到底在考什么1.1 从一道模拟题看真实机试的题感华为机试不是竞赛也不是纯粹的LeetCode。它更像是在限时条件下考察你能不能把工程里的常见小问题用代码快速解决。我经历的批次一般是三道编程题难度从简单到难逐步增加最后一道往往不是纯算法题而是某种业务场景的简化版本。比如“从一堆日志里统计出满足条件的记录”“给一批任务排优先级”“在停车位里选一个最优空位”等等。你会发现这些题目描述都很长样例也有好几个其实核心算法并不复杂。真正的难点在于从一段啰嗦的业务描述里快速提取出数据结构、输入输出和约束条件。模拟题的价值就体现出来了它帮你训练这种“翻译”能力而不是单纯训练算法思维。我第一次做模拟题3的时候题目并不难但我花了二十分钟才搞明白输入格式然后因为没处理空行直接报错。后面练多了看到“以逗号分隔的字符串”这种描述第一反应就是split后strip看到“多行输入”就立刻想到循环读行。这些反应都是靠模拟题一次次喂出来的。什么叫“题感”就是看到题目描述里的某几个词脑子里自动跳出对应的处理方案。比如看到“连续”想滑动窗口看到“最近”想BFS或预处理看到“第k大”想堆或二分看到“所有可能”想回溯或动态规划。这种自动反应不是天生的完全靠大量模拟题训练出来的。真正上考场时大部分时间都在写代码留给你慢慢“想为什么”的时间很少题感决定了你的第一反应对不对。1.2 为什么模拟题比海量刷题更值得做很多人备考华为机试的首选是刷LeetCode这没有错但只刷LeetCode远远不够。因为机试平台和LeetCode不同它的输入输出需要自己从标准输入读结果要自己用print输出。LeetCode已经帮你把函数封装好了但华为机试更多是“写一个完整程序”的模式。我见过不少朋友LeetCode刷了三百多题结果第一次做华为模拟题时连输入都没搞对。原因很简单平时习惯了函数式作答一旦面对“解析一行含逗号和引号的字符串”“读取可能包含空行的多行数据”等场景脑子就短路了。模拟题恰好能补齐这个短板。另一个原因是模拟题会刻意加入边界条件。比如字符串长度为1的情况、数组内没有某个元素的情况、输入数据可能含空格的情况。这些边界条件在LeetCode题目里也有但那种“一道题只有一个函数、参数已经定好”的形式会让人忽略真实输入带来的额外复杂性。模拟题逼你处理这些脏活累活而这才是机试真正的决胜点。我身边有个朋友LeetCode中等题都能独立写出来但模拟考每次都差一点。后来我帮他复盘发现每次都是死在输入解析和输出格式上。有一次题目要求输出一行数组元素以空格分隔他直接print了一个Python列表输出成了[1, 2, 3]判题直接不给过。这种错误刷LeetCode永远发现不了只有做模拟题才能暴露。2. 拆解题型与算法考点分布2.1 高频考点Top6华为机试的算法考点其实非常集中。根据我刷过的模拟题和身边朋友反馈反复出现的主要有这几类数组与字符串处理字符串分割、拼接、去重、排序、匹配。这类题占比最大几乎每场必考。比如给一个IP地址格式的字符串让你判断是否合法给一串用分号隔开的键值对按规则排序。这些题没有高深算法但特别考验细心程度。哈希表统计频次、判断是否重复、快速查找。一般会和数组、字符串结合出题。比如统计一个字符串里出现次数最多的字符或者判断两个数组是否有交集。双指针与滑动窗口求最长无重复子串、满足条件的连续子数组、数组的两数之和等。这类题在模拟题里出现频率很高因为代码量不大但思路很灵活。排序与自定义排序按对象的某个字段排序注意机试常需要自己写比较函数。华为机试最喜欢考“按某种规则排序”比如按文件大小排序、按学生成绩排序排序规则经常是一句话能说清但写起来很容易漏条件。动态规划背包问题、最长递增子序列、编辑距离、爬楼梯变种。这类题通常作为压轴题出现状态转移方程如果没想清楚很容易写出超时的暴力递归。图与搜索BFS/DFS求连通块、最短路径、岛屿数量等。出现频率不如前几类但遇到就是硬仗特别是矩阵类题目边界处理非常烦。不要小看“字符串处理”很多第三题看起来高大上最后一步就是排序哈希。把基本功练扎实比去钻冷门算法划算得多。2.2 冷门但容易翻车的考点除了高频考点还有几个出现频率不高但一旦出现就让人翻车的点。第一是位运算。我曾做过一道模拟题要求用O(1)空间找出数组中出现奇数次的数字解法就是异或。如果不熟悉位运算可能就只会用哈希表然后被空间限制卡死。位运算题目通常代码极短但需要你理解异或、与、或、移位这些操作的本质考前花半天把常见位运算技巧过一遍性价比很高。第二是前缀和与差分。区间求和、区间增减这类题如果数据范围到10^5暴力一定超时前缀和就是标准解法。这类题看起来像数学题其实套路很固定。比如“一个数组执行多次区间加1操作问最终数组”这类差分数组能轻松解决。第三是状态压缩。当旅行商、集合覆盖这类问题出现时大概率需要用状态压缩DP。不过这类题在华为机试里极少见我建议作为进阶内容不要一开始就死磕。如果你目标只是通过机试把时间花在高频考点上更划算。我的经验是先把高频考点的模板背到肌肉记忆再花少量时间扩展冷门考点。不要本末倒置毕竟备考时间有限。为了让复习更有针对性我自己整理过一张考点权重表大致长这样考点出现概率建议投入时间典型题型数组/字符串极高40%排序、去重、IP校验哈希表高20%频率统计、是否存在双指针/滑动窗口高15%最长无重复子串、两数之和动态规划中高15%背包、递增子序列、编辑距离DFS/BFS中5%岛屿数量、最短路径位运算/前缀和低5%异或找唯一数、区间更新这个表不一定准确但能帮你避免把时间浪费在概率极低的难题上。3. 一道典型模拟题的全过程拆解3.1 题目重述与输入输出约束下面这道题是我从“华为机试编程模拟题3”的练手场景里提炼出来的很有代表性。题目描述停车场有一排车位车位从左到右编号为1到N。其中有些车位已经停了车用字符1表示空位用0表示。现在有一辆新车要停入停车场要求停在一个空位并且这个空位到最近一辆已有车的距离尽可能大。如果有多个满足条件的空位输出编号最小的那个。若没有空位输出-1。输入是一行只包含0和1的字符串长度不超过100000。样例输入10001。解释编号1有车编号5有车中间编号2/3/4都是空位。编号2离1的距离为1编号4离5的距离为1编号3离1和5的距离都是2所以最优位置是3输出3。看到这个题先别急着写代码。第一步是明确输入输出格式。输入只有一行用input().strip()读进来输出是一个整数末尾换行。长度到100000说明O(n^2)的暴力算法大概率超时必须想O(n)或O(n log n)的解法。还需要理解清楚“距离”的定义。题目说的是“到最近一辆已有车的距离”所以一个空位可能左边有车右边也有车它和最近那辆车的距离是左右两边距离里的较小值。目标则是让这个较小值尽可能大。这本质是在最大化“最小间隔”是一个典型的贪心预处理问题。3.2 暴力解法与线性解法的思路对比最直接的想法是枚举每一个空位然后向左右两边逐个找最近的1计算距离。空位每查一次最坏情况下要扫描整个数组所以总复杂度是O(n^2)。当n10000时操作量是1亿在Python里已经很危险当n100000时10亿次操作基本不可能通过。那怎么优化把“每个位置最近一辆车的距离”预先算出来。可以维护两个数组left和right。第一次从左往右遍历left[i]记录位置i左边最近的1的下标第二次从右往左遍历right[i]记录位置i右边最近的1的下标。这两个数组都填充完成后再遍历一遍所有空位对每一个位置i如果left[i]存在左边距离就是i - left[i]如果right[i]存在右边距离就是right[i] - i取二者中较小的那个就是该位置到最近车辆的距离所有空位中取距离最大的因为从左往右扫且只有严格大于才更新所以多个最大值时自然保留最左边的。这个解法的时间复杂度是O(n)空间复杂度也是O(n)。在n100000时完全没压力。核心思想就是“预处理一次枚举”几乎适用于所有“需要反复查询某个区域信息”的题目。我再具体算一笔账。假设n100000暴力法每个0位置都要左右找最坏情况整个数组全是0每个位置扫描n次总操作量10^10。哪怕机器每秒执行10^8次简单操作也需要100秒远超机试的时限。而线性解法则只有三轮循环每轮10万次总共30万次操作几毫秒就能完成。这就是为什么考场上必须对数据规模保持敏感看到100000就要立刻排除O(n^2)。3.3 可落地代码实现Python版 C关键点我用Python写了一个完整版本可以直接跑def solve(): s input().strip() n len(s) left [-1] * n right [-1] * n last -1 for i in range(n): if s[i] 1: last i left[i] last last -1 for i in range(n - 1, -1, -1): if s[i] 1: last i right[i] last best_pos -1 best_dist -1 for i in range(n): if s[i] 0: dist n if left[i] ! -1: dist min(dist, i - left[i]) if right[i] ! -1: dist min(dist, right[i] - i) if dist best_dist: best_dist dist best_pos i if best_pos -1: print(-1) else: print(best_pos 1) if __name__ __main__: solve()几个需要注意的点left和right数组初始化成-1表示“该方向上没有车”。dist初始化为n因为最远距离也不可能超过n这样即使两边都没有车dist也是n不会影响最终比较。最后输出best_pos 1因为题目编号从1开始。如果best_pos仍是-1说明没有空位直接输出-1。如果用C写核心思路一样。读入用getline(cin, s)然后vector填充。唯一要留神的是字符串长度可能很大不要用char数组定死大小直接用string。比较逻辑和Python完全一致。C实现里有两个易错点一是vectorint left(n, -1)初始化一定要放在读入字符串之后否则n不确定二是在循环里别把left[i]写成left[i-1]虽然思路是滚动更新但数组版我们存的是i位置的值不是递推值。这种细节错误在紧张时特别容易犯写完最好逐行读一遍。3.4 边界条件与测试用例我模拟了程序跑几个典型用例的结果输入输出说明100013中间位置离两边都是2最优10012编号2和3距离都是1取最左编号20001没有已有车辆所有空位距离都按无穷大处理取最左111-1没有空位1-1只有一个车位且已有车01只有空位停在1号10000000016两车之间8个空位中间两个位置距离4取最左如果题目规定输入里必须至少有一辆车那“000”这个用例可以忽略。但自己写代码时把这种情况处理掉总是更稳妥。这也是一条通用经验不要依赖题目没写明的假设多防御一个边界可能就多拿一个用例的分。设计自测用例的时候我有个习惯先按正常情况测一组再按最小输入测一组再按极端输入测一组。正常情况保证算法正确性最小输入测试边界极端输入测试性能。比如这个题最小输入就是长度为1的字符串极端输入就是长度100000且只有首尾有车的字符串。用这三个维度去测比盲写十个用例覆盖得还全。4. 实战中的踩坑记录与排查方法4.1 输入输出格式的坑我踩过最多次的坑十有八九都在输入输出上。华为机试的输入格式变化很多有的题目只有一行简单字符串有的题目有t行数据更有一些题目会故意在行尾带上空格或空行。应对方法很简单所有字符串在读取后都做一次strip()如果是按行读用sys.stdin.read().splitlines()或者while True逐行读但要明确终止条件。还有输出格式。要求输出小数时不能直接print浮点数然后用默认精度要求输出排序后的数组可能要用空格分隔而不是逗号。这些细节往往被样例覆盖但很多时候样例只有一个其他用例的格式需要自己推理。我的习惯是准备几个输入输出模板代码考前默写一遍考试时就能少花时间。比如输出数组Python里可以这样arr [1, 2, 3] print( .join(map(str, arr)))而不是直接print(arr)因为后者会输出[1, 2, 3]。这种基础模板不要到了考场才想考前就应该记熟。另外如果题目是“多组输入直到文件结尾”用while True:加try/except EOFError处理在Python里很常见但要注意别因为空行导致死循环。C则是while(getline(cin, s))。4.2 超时与内存超限的排查模拟题和正式机试一样有严格的时间和内存限制。如果你提交后提示超时先不要急着优化代码细节而是重新看复杂度。我通常问自己三个问题这个算法是不是把数据完整遍历了一遍有没有用一个循环套另一个循环可不可以把重复计算缓存下来停车位那道题如果一开始写的是O(n^2)暴力在小数据量下没问题但一旦长度到100000超时是必然的。换成预处理后变成三次循环每次都是O(n)总共3n步完全没问题。有时候超时不是算法复杂度的问题而是语言层面写得太慢。比如Python里频繁用input()读大量数据每读一行就做一次系统调用性能很差。我一般用import sys然后sys.stdin.readline替代。还有把循环里不变的属性计算放到循环外缓存比如把s[i]取到局部变量能省不少时间。内存超限一般出现在两块一是不小心把二维数组开得过大二是递归深度太深。遇到“二维矩阵”问题不要上来就开一个n x m的vector先想想能不能用滚动数组或者只存一行。遇到DFS/BFS如果题目数据范围大优先考虑用栈/队列模拟而不是递归防止爆栈。4.3 新系统双机位下的答题节奏最近不少考生讨论的“新系统双机位c卷”本质上就是机试环境升级后的考务要求。双机位意味着后置摄像头要拍到你的双手和屏幕C卷只是题库的一个卷别代号。这些变化对应试能力的要求没变但对答题节奏和考前准备提出了新要求。第一考前一定要按官方要求调试摄像头、浏览器、网络。不要等到开考时才发现设备有问题。第二开考后不要有任何切屏行为哪怕是误触。第三合理分配时间我的策略是先把三道题都读一遍按难度排序先做最容易拿分的题再做中等题最后啃硬骨头。每道题做完后至少留出几分钟自测样例和边界用例。我自己的时间分配大致是这样如果总时长150分钟前面10分钟通读所有题第一题最多30分钟第二题最多40分钟第三题最多50分钟剩下20分钟做全场检查。一旦发现某道题卡了超过预期时间果断跳到下一题不要恋战。平时做模拟题也按这个节奏来到考场上才不会慌。有些考生担心新系统会不会和平时刷题平台不一样导致操作不习惯。解决办法很简单考前几天用官方指定的模拟环境或同类在线平台完整走一遍“阅读题目→编写代码→提交判题”的流程。见过太多人因为不熟悉平台把时间浪费在调试编辑器格式上。5. 刷题策略与工具选择5.1 刷题顺序怎么安排如果距离考试还有一个月以上我建议按“基础数据结构→进阶算法→模拟套题”的顺序来刷。第一周先把数组、字符串、哈希表、排序这些基本功过一遍用LeetCode或力扣的标签筛选“简单”和“中等”题目第二周集中练双指针、滑动窗口、BFS/DFS第三周开始每天做一套华为机试模拟题完整计时把每道题都当成正式考试来写第四周回头看错题和模板。如果只剩一周那就别贪多。每天精做一套模拟题把不会的题归类然后针对薄弱点补固定模板。比如动态规划不会就把“最长递增子序列”“01背包”“编辑距离”三道题各写三遍直到不看答案能默写。这里要特别强调“精做”和“做对”的区别。一道题如果AC了但其实是蒙对的那不算掌握。我会强迫自己写完代码后口述一遍解题思路包括为什么用这个算法、边界条件怎么处理、有没有更优解。能讲清楚才是真会了。5.2 用AI编程辅助但不依赖现在AI编程工具很火比如Cursor、GitHub Copilot之类。我在备考时也用过AI来辅助让AI解释一道题的思路、帮忙生成测试用例、对比不同解法的复杂度。这些确实能提高效率但有一个底线——考试环境里这些工具都不存在所以平时写代码还是要自己动手。我的建议是先用AI读懂题目和思路然后关掉AI自己把代码完整写出来。如果卡住了再看AI的提示但看完后一定要自己再写一遍。用这种“AI辅助但不依赖”的方式既能学到思路又能保持手写代码的手感。千万不能变成“AI写代码我复制”那样换到考场就会原形毕露。比如遇到一道完全没思路的题你可以把题面丢给AI让它给出“暴力解和优化解”两种方案。你读懂了思路之后不要急着看它的代码而是自己尝试实现。实现过程中卡壳再回头看它怎么处理细节。这样一个来回下来你对这个类型题目的理解会比直接抄答案深得多。5.3 考前最后一周做什么最后一周我基本不再碰新题。每天只做三件事一是默写常用输入输出模板包括字符串分行处理、多组输入、二维数组读取二是默写高频算法模板比如排序、二分、滑动窗口、DFS/BFS、标准动态规划三是重做之前的错题尤其是那些因为边界条件写错而没AC的题。另外考前一定要亲手在模拟平台上测一遍。有的平台支持自测输入输出有的只支持在线判题。把平台的编辑器、快捷键、切换输入法的习惯都提前适应一遍。很多考生不是因为不会写代码挂的而是因为不熟悉平台浪费了大量时间在调格式和查错上。如果你时间充裕还可以自己构造几个极端测试用例比如长度最大、全是相同字符、空输入等验证代码的健壮性。这个习惯一旦养成不仅能提升机试成绩对以后工作中的代码质量也有帮助。我自己有一个“复盘模板”每套模拟题做完后用三行字记录一是今天的题属于哪个考点二是踩了什么坑三是下次如何避免。比如“停车位属于数组预处理踩坑没有处理全0情况下次遇到距离最远问题先想左右预处理”。考前翻一遍这些小卡片比重复刷题更有效。备考华为机试心态上也要稳。我见过太多人一上来就追求难题结果简单题反而不稳。其实机试的核心是“把会做的题做对”而不是“把不会做的题做出来”。与其死磕一道冷门难题不如把常考的数组、字符串、哈希、双指针练到条件反射。模拟题就是练这种条件反射最好的工具。最后再分享一个小技巧做模拟题时故意在一个安静、有摄像头、不能切屏的环境里练习。第一次可能很不适应但这种不适应正式考试时会更严重。提前适应把环境因素变成可控项你的真实水平才能在考场上完整发挥出来。