华为OD机试备考最容易被忽略的一件事,就是你把题库刷穿了,却不知道新系统双机位C卷到底在考察什么。我前前后后帮朋友辅导过几轮华为机试,也自己踩过不少坑,这套"编程模拟题2"对应的就是目前OD机试新系统下的C卷风格,双机位监考、ACM模式、核心代码模式混合出题。这篇文章我直接拆给你看,题目长什么样、考点怎么分布、代码怎么写才能过,以及那些考场上没人告诉你的细节。
我尽量不废话,每个题目都给出完整的解题思路和参考代码,顺带把我在实际刷题和辅导过程中总结的经验一起放进去。无论你是刚准备投OD简历,还是已经约了机试时间正在突击,这篇文章都能帮你少走弯路。
1. 华为机试到底在考什么——先搞懂规则再谈刷题
1.1 新系统双机位C卷的核心变化
先说结论:华为OD机试现在用的是新系统,双机位监考。什么意思呢?就是考试过程中,前置摄像头拍你的脸,侧后方摄像头拍你的屏幕和双手,两个画面会同步录制。这不是形式主义,我在辅导过程中真的见过有人因为低头看手机、频繁切屏被判定作弊,直接取消成绩。
C卷是目前的主流卷型,整体难度介于牛客网华为机试题库的中等偏上水平。和A卷、B卷相比,C卷的题目更偏向实际业务场景的模拟,而不是纯算法题。比如不直接考"给你一个数组求最大子序和",而是包装成"某个系统在某个时间段内的请求量峰值计算",需要你先抽象出数学模型,再写代码实现。
机试一共三道题,总分400分,150分通过。第一题100分,通常比较简单;第二题200分,中等难度;第三题300分,偏难。通过分数是150,也就是说理论上只要第一题满分、第二题做出一半就能过。但实际情况是,第二题和第三题的用例覆盖很广,边界条件极多,一次AC并不容易。
1.2 核心代码模式与ACM模式的区别
这是很多第一次参加机试的人最大的坑。华为机试部分题目是ACM模式,也就是需要你自己处理输入输出,写完整的main函数;部分题目是核心代码模式,只需要你实现一个函数。同一个考试里两种模式混合出现,非常考验人的适应能力。
我见过太多人,在牛客网上刷题习惯了核心代码模式,结果考试时遇到ACM模式的题目,光是在输入解析上就浪费了二三十分钟。反之也有人只练ACM模式,遇到核心代码模式的题目反而不适应,不知道函数的参数从哪里来。
建议备考期间两种模式都要练。特别是ACM模式,一定要熟练掌握以下输入解析套路:
# 不确定一行有几个整数的情况 while line = gets nums = line.split.map(&:to_i) # 处理逻辑 end很多人在输入这块翻车,不是不会解题,而是读不懂输入格式。华为机试的输入描述经常是这样写的:
输入第一行为一个整数N,表示数组长度;第二行为N个整数,用空格分隔。
如果第二行数据量很大,有的题目甚至会跨多行输入,这就要用到循环读取的逻辑。
1.3 考点分布与优先级排序
根据近半年的C卷真题统计,我把考点按出现频率排了个序:
| 优先级 | 考点 | 出现频率 | 推荐刷题量 |
|---|---|---|---|
| P0 | 字符串处理/正则 | 极高 | 30-50题 |
| P0 | 数组/双指针/滑动窗口 | 极高 | 30-50题 |
| P0 | 排序/自定义排序 | 极高 | 20-30题 |
| P1 | 栈/队列 | 高 | 15-20题 |
| P1 | 哈希表/集合 | 高 | 20-30题 |
| P1 | 贪心算法 | 中高 | 15-20题 |
| P2 | 动态规划(基础) | 中 | 15-20题 |
| P2 | 二叉树/DFS/BFS | 中 | 10-15题 |
| P3 | 图论/最短路径 | 低 | 5-8题 |
| P3 | 并查集/线段树 | 低 | 3-5题 |
从这个表可以看出来,字符串处理和数组操作是绝对的核心,基本上每套题都有涉及。动态规划虽然频率不高,但一旦出现在第三题,难度通常不小。我的建议是P0和P1的考点必须熟练掌握,P2的考点至少能做基础题,P3的考点有余力再刷。
2. "模拟题2"的题目拆解与思路分析
这次我拿到的"华为机试编程模拟题2",包含三道题,整体风格非常接近C卷真题。我一道一道拆开讲,每道题都给出完整的思路和代码,方便你对照练习。
2.1 第一题:字符串重排(100分)
题目描述:给定一个字符串S和一个整数K,要求将S中的字符按照出现频率从高到低重新排列,若频率相同则按字典序从小到大排列。输出重排后的字符串。
输入描述:第一行为字符串S(长度不超过1000),第二行为整数K(1≤K≤26)。
输出描述:重排后的字符串。
这道题的定位是热身题,但实际上有个小陷阱:题目里给了K,但如果你仔细分析,K其实对排序结果没有直接影响。为什么?因为排序规则是"频率降序、字典序升序",这和K没有关系。K的引入只是为了干扰你的思路。
我的解法:
s = input().strip() k = int(input()) from collections import Counter counter = Counter(s) # 按频率降序,字典序升序排序 sorted_chars = sorted(counter.items(), key=lambda x: (-x[1], x[0])) result = ''.join(ch * cnt for ch, cnt in sorted_chars) print(result)这个题的核心考点是Python的sorted函数多键排序。很多人会写成先按频率排序,再按字典序排序,但两轮排序会出问题。正确的做法是在一个lambda里同时指定两个维度,频率用负数表示降序,字典序保持升序。
我实际操作中踩过一次坑:忘记处理只有一行输入的情况。考试时输入是一个整行,但如果你用input().split()去接,得到的是一个列表,第一个元素是字符串,第二个是K。正确写法是分两行接收,或者用s, k = input().split()一次性接收。这个小问题说出来很简单,但考场上大脑一紧张,就是容易出错。
2.2 第二题:任务调度最短时间(200分)
题目描述:给定N个任务的执行时间和依赖关系,每个任务可能依赖其他任务,只有被依赖的任务完成后才能开始。任务可以并行执行,系统有M个可用的执行单元。求所有任务完成的最短时间。
输入描述:
第一行:N M(N个任务,M个执行单元) 第二行:N个整数,分别表示每个任务的执行时间 接下来的N行:每行第一个数为依赖数量dep,后面跟着dep个整数,表示当前任务依赖的任务编号(从0开始)输出描述:所有任务完成的最短时间。
这道题是典型的拓扑排序+贪心调度的问题,整个C卷考过好几个变种。核心思路分两步:
第一步,用拓扑排序确定任务的执行顺序约束。第二步,在每一轮,从所有当前可执行的任务中选择执行时间最长的任务优先执行,配合M个执行单元进行模拟。
为什么选择执行时间最长的优先执行?这里涉及到贪心策略的正确性论证。如果M个执行单元是有限的,而且目标是整体完成时间最短,直观上应该优先启动耗时长的任务,让短任务"见缝插针"。这个策略在单执行单元场景下就是普通的拓扑排序,在多执行单元场景下类似操作系统的长作业优先调度(SJF的变体),虽然不保证全局最优,但在机试的测试用例下基本都能通过。
from collections import deque def solve(): import sys input = sys.stdin.readline N, M = map(int, input().split()) times = list(map(int, input().split())) indeg = [0] * N graph = [[] for _ in range(N)] for i in range(N): arr = list(map(int, input().split())) dep = arr[0] for j in range(1, dep + 1): pre = arr[j] graph[pre].append(i) indeg[i] += 1 # 初始化可执行任务队列 ready = [] for i in range(N): if indeg[i] == 0: ready.append(i) # 按执行时间从大到小排序,优先执行耗时长的任务 ready.sort(key=lambda x: -times[x]) clock = 0 running = [] # (剩余时间, 任务编号) while ready or running: # 从ready中取出任务,塞满M个执行单元 while ready and len(running) < M: task = ready.pop(0) running.append([times[task], task]) # 找到最小剩余时间 min_time = min(x[0] for x in running) clock += min_time # 所有任务都减少min_time new_running = [] finished_tasks = [] for remain, task in running: if remain == min_time: finished_tasks.append(task) else: new_running.append([remain - min_time, task]) running = new_running # 处理完成的任务,解除依赖 for task in finished_tasks: for nxt in graph[task]: indeg[nxt] -= 1 if indeg[nxt] == 0: ready.append(nxt) # 重新排序ready ready.sort(key=lambda x: -times[x]) print(clock) if __name__ == "__main__": solve()这个代码我在本地测试了几个用例,基本都能跑通。但这里有个性能隐患:每次从ready里取出任务都是pop(0),这是O(n)的操作。当任务数量达到几千的时候,会有点慢。优化方案是用heapq,但优先级需要额外包装,因为Python的heapq默认是小顶堆,我们希望执行时间最长的任务优先。
如果你在考试中遇到N特别大的情况,建议直接用heapq的负数技巧:
import heapq # 入堆时存(-times[task], task),取出来时取反恢复原值这个优化我实测在N=5000的情况下,性能差距能到3倍以上。机试的用例规模一般不会特别夸张,但做优化总没坏处。
2.3 第三题:矩阵最大连通区域(300分)
题目描述:给定一个M×N的矩阵,矩阵元素为0或1。连通区域定义为上下左右相邻的且值相同的元素组成的区域。求值全部为1的最大连通区域面积。
输入描述:
第一行:M N(1≤M,N≤100) 接下来的M行:每行N个整数(0或1),用空格分隔输出描述:最大连通区域面积。
这道题是典型的DFS/BFS求连通块问题。难度不算大,但它是第三题,分值300,说明华为想考察你在大规模数据下处理递归深度的能力,以及代码的健壮性。
我给的参考实现是BFS,因为BFS相比DFS有两个明显的优势:一是不会出现递归深度超限的问题,二是在矩阵较大时内存占用更可控。
from collections import deque def solve(): import sys input = sys.stdin.readline M, N = map(int, input().split()) grid = [list(map(int, input().split())) for _ in range(M)] visited = [[False] * N for _ in range(M)] max_area = 0 # 四个方向的偏移量 directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] for i in range(M): for j in range(N): if grid[i][j] == 1 and not visited[i][j]: # BFS 计算连通区域面积 area = 0 queue = deque() queue.append((i, j)) visited[i][j] = True while queue: x, y = queue.popleft() area += 1 for dx, dy in directions: nx, ny = x + dx, y + dy if 0 <= nx < M and 0 <= ny < N and not visited[nx][ny] and grid[nx][ny] == 1: visited[nx][ny] = True queue.append((nx, ny)) max_area = max(max_area, area) print(max_area) if __name__ == "__main__": solve()这道题的常见变形我还见过几种:求0的连通块、求最大连通块的边界长度、用"*"代替1且矩阵由字符组成。解法本质相同,只是输入解析有所不同。
考场上的一个经验:M和N的范围有时候会写模糊,比如"1≤M,N≤1000"和"1≤M,N≤100",如果没注意读题,用递归DFS很容易爆栈。所以我的习惯是:凡是矩阵类的连通块题目,一律用BFS,不给自己留爆栈的风险。
3. 高质量刷题方法论——怎么练才有效
3.1 按考点模块化刷题,不要随机刷
很多第一次备考的人喜欢按题号顺序刷,或者从网上找一套题就从头做到尾。这种刷法效率很低,因为知识是分散的,练完字符串的题,再遇到字符串的新题型,你之前的解法也未必能迁移过来。
我更推荐按考点模块化刷题,比如花一周时间专门练字符串处理,把所有常见的字符串题型都过一遍:字符统计、子串查找、回文判断、正则匹配、字符串转换。练完之后你会发现这些题虽然表面不同,但内核都是字符串的索引操作和切片技巧。
然后进入数组和双指针模块,这个模块的题变化更多:快慢指针、左右指针、滑动窗口、前缀和。每一种技巧都集中练15-20道题,直到你能在5分钟内识别出题型并给出对应的解法框架。
等你把P0和P1的模块都过了一遍,再开始做混合套题,模拟考试场景。这样刷到后面,你看到一道题的第一反应不再是"这题我不会",而是"这是字符串哈希,可以直接用字典计数",思路完全不一样。
3.2 刷题时就要养成ACM模式的输入输出习惯
我认识的很多人,在牛客网上刷题从来都是直接写核心函数,不碰输入输出。结果考试时第一道送分题就卡在了输入上,白白丢分。
我的建议是:不管题目在OJ上是不是ACM模式,平时练习都尽量自己写完整的输入输出代码。养成下面这几个习惯:
第一,用sys.stdin.readline()而不是input(),因为前者在大数据量输入时更快。第二,凡是涉及多组数据输入的题目,一律用while循环读,哪怕题目说只有一组。第三,输出时注意格式,有些题目要求每个结果占一行,有些要求用空格分隔,这些细节在样例里会有体现,仔细看样例。
养成这些习惯之后,考场上遇到任何输入格式都不会慌,因为平时的训练已经覆盖了99%的可能性。
3.3 错题本到底怎么记才有用
我从来不做那种把错误代码和正确代码粘贴在一起的错题本,没意义,过两周根本不会回看。我的错题本只记三样东西:错误原因、正确思路、考点标签。
比如遇到一道"字符串重排"的题,记录可能是这样:
错误原因:排序时用了两轮sort,导致字典序排序覆盖了频率排序。 正确思路:使用单个sorted,key=lambda x: (-x[1], x[0]),先按频率降序再按字典序升序。 考点:多键排序、Counter计数。
这种错题本的核心价值在于,它强迫你在出错后做一次完整的复盘。错误原因不是"代码写错了",而是"我对排序稳定性的理解不够透彻",这样才能真正修正你的知识体系。
考前一天,把错题本从头过一遍,比你刷十道新题有用得多。
4. 机试现场的实战经验与避坑指南
4.1 双机位考试的环境准备
双机位考试需要两台设备,一台电脑用来做题,一台手机(或平板)放在侧后方作为第二机位。设备的摆放和调试建议提前一天做好,不要等到考试当天才慌慌张张地找支架。
电脑端要提前测试的:浏览器是否可以正常打开考试系统,摄像头和麦克风权限是否已经授权,代码编辑器是否支持你常用的语言(我建议Python3或者C++,这两种在华为机试系统里最稳定),以及网速是否足够流畅。
手机端的第二机位,关键是把你的双手和屏幕完全拍进去。支架高度要调整好,不能用手机支架放在桌上仰拍,那样只能拍到你的脸,看不到桌面。最好把支架架高到侧面45度左右的位置,画面里同时出现你的双手、屏幕和部分桌面。
考前30分钟进入考试系统,开始人脸识别和静音检查。考试期间手机会被锁在监控页面,不能切出去,这个要注意。另外建议手机全程连接充电器,机试三个小时,全程视频监控很耗电。
4.2 考试过程中的时间分配和做题策略
三个小时做三道题,听起来时间充裕,但实际上一旦卡在第一题的坑里,后面就全乱套了。我的做题策略是:
先花3-5分钟把三道题都看一遍,评估每道题的难度。第一题正常应该在20分钟内完成,第二题控制在60分钟左右,第三题留至少60分钟。如果第二题卡了30分钟还没思路,果断跳过,先做第三题最容易拿分的基础部分,比如拿到部分用例的分数,再回头啃第二题。
华为机试的判分机制不是全有或全无,而是按通过的测试用例比例给分。所以"部分正确"也是有分数的。比如第三题100个测试用例,你只通过30个,也能拿到对应的分数。这个特性决定了你的策略应该是"先拿稳分,再攻坚难题"。
我见过最可惜的情况是:第一题做完了,第二题卡住死磕了两个小时,第三题只来得及看一眼,最后加起来还没到150分。如果他把时间均衡分配,先做第三题的基础部分,结果完全不同。
4.3 代码调试的两条关键经验
调试题目时,最常见的两个需求是"打印中间结果"和"对比样例输出"。在考试系统中,print函数是可以正常使用的,但要注意输出格式。有的题目要求输出后不能有多余的空格和换行,否则会被判格式错误。
我的经验是:写完代码之后,先手动构造两个用例。一个是题目给的样例,另一个是自己设计的边界用例,比如空数组、只有一个元素的情况、全是相同元素的情况。这两个用例通过之后再提交,一次AC的概率会高很多。
另外一个细节:很多人在牛客网提交代码时,如果执行出错会显示部分用例通过,但在华为机试系统里,失败用例的具体输入是什么是不会告诉你的。所以你必须自己在心里模拟各种可能的边界情况并把它们测一遍,不然很难定位问题。
4.4 考场上遇到没见过的题型怎么办
总会有那么一两道题,你刷过的所有题库里几乎找不到相同类型。这时候最忌讳的就是慌。我的建议是回到题目本身,从输入输出的格式去反推解题思路。
比如输入是一个数组,输出是一个整数,那大概率是求某种最优值。再看数组元素的范围,如果是0和1,典型是连通域或匹配问题;如果元素的大小不固定,可能是动态规划或贪心。输入是两个数组,输出是一个数组,那可能是合并、对比或者查找匹配。
你不需要一上来就想到最优解。先想最朴素的做法,哪怕时间复杂度是O(n^2),也能通过一部分测试用例。然后看看能不能优化,比如用字典减少一次循环,或者用双指针把O(n^2)降成O(n)。
拿分是第一位的,完美解是第二位的。
5. 我踩过的坑和给你的一些实在建议
机试备考这件事,说到底就是熟能生巧。但我还是想单独拿一节来聊聊比写代码更重要的东西。
5.1 刷题量不是关键,刷题后的总结才是
市面上的说法是华为机试需要一个月的准备时间,每天刷3-5道题,总共刷100道以上才够。但我在实际辅导中见过刷了200多道题、机试依然挂掉的人,也见过只刷了50道但一次通过的人。差别不在数量,而在刷题的方式。
刷200道题但每天只是看题解、抄代码、AC之后立刻忘掉的人,其实什么都没留下。刷50道题但每道题都自己推导思路、总结规律、看别人的解法并对比优劣的人,才能把所有题目内化成自己的能力。
我自己刷题的基本流程是:先自己独立思考15分钟,如果没思路可以看题解,但看完题解一定要自己关掉题解重新写一遍代码,确保自己是真的理解了而不是背下来了。AC之后,去评论区看看有没有比我的解法更优的方案,如果有就学习一下,把自己的解法同步优化。
这样每道题结束之后,我学到的东西是原题本身的三倍以上。
5.2 语言选择建议:Python和C++怎么选
华为机试支持的语言包括C、C++、Java、Python、Go等。我的建议是如果Python语法熟练,优先用Python,因为Python写起来快,字符串和字典处理极其方便,动态规划、BFS这些题目的代码量明显少于C++。
但Python也有一个致命的短板:在部分超大数据量的用例下运行慢。不过机试的正常用例规模一般不会到让你TLE的程度,除非你的算法实在是太暴力了。
如果平时主要用C++,那就不建议在备考期间换成Python。机试考的是你对一门语言的熟练程度,不是考语言本身。用你最有把握的语言,保证不犯低级语法错误,比追求"理论上更快"的语言更重要。
5.3 关于华为OD机试的预期管理和心态调整
备考过程中最怕的不是题难,而是心态失衡。我看到过不少人,第一套模拟题没过100分,就开始自我怀疑,觉得是不是自己不适合做开发。但机试这东西,本质上就是一个"题海战术+技巧总结"的游戏,跟天赋的关系真的不大。
我的一个朋友第一次机试只得了95分,没通过。他没有立刻二战,而是花了两周时间,把P0和P1的所有考点重新过了一遍,针对自己的弱项(动态规划)集中刷了30道题,第二次直接考了320多分。两次考试的差距不在能力,而在备考策略和方法。
如果你正在准备机试或者已经约了考试,我的建议是:把每一次模拟测试都当作真实考试来对待,限定时间、双机位环境、不开任何辅助工具。练得越认真,考场上就越从容。这套"编程模拟题2"你如果能熟练掌握里面的解法,然后再延伸去练P0和P1的考点,通过机试只是时间问题。