ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

华为机试模拟题第6套:字符串、DP与任务调度精讲

华为机试模拟题第6套:字符串、DP与任务调度精讲 华为机试的模拟题我一直在持续更新这一套编号做到第6套正好赶上不少朋友在准备新一轮的OD招聘季也有部分是校招同学用来练手的。先说结论这套模拟题按目前新系统C卷的考法设计三题分布是100分100分200分覆盖字符串处理、经典动态规划和带依赖的任务调度基本把华为机试最常见的几类考点都踩中了。不管你是刚开始刷题、还是已经刷了一两百道想检验状态这套题都值得按真实考试节奏做一遍。下面我直接把这套题的考点、完整思路、参考代码和实战坑位都拆开讲。1. 华为机试到底是什么先从考试规则说起先说最基础的很多人第一次听说华为机试其实是因为投了OD岗位然后发现流程里有一道机考。实际上华为的正式校招和社招也有机试环节只是题源和通过标准略有差异。OD机试目前用的是新系统要求双机位也就是主摄像头拍你正面副摄像头从侧后方拍你的屏幕和桌面考试前还会让你用手机扫环境视频。这套机制说白了就是防作弊但也意味着你的考试环境必须提前准备好房间光线正常、桌面没有杂物、网络稳定、设备给足权限。别小看这一步真有朋友在考前半小时发现摄像头权限没开折腾半天才进去。考试本身的规则比较固定一共3道编程题时长2小时总分400分。第1题100分难度偏基础基本是送分题第2题100分中等难度开始需要你有一点算法积累第3题200分综合题通常会结合两个以上知识点也是拉分的关键。通过线在不同部门不一样有的150分就行有的要200分以上但普遍的说法是前两道题稳稳拿满第三题做出部分用例即可。这里我多说一句机试的判题规则是按通过用例比例给分的第三题哪怕只过掉30%的用例也能拿到不少分数所以完全没有思路时不要直接放弃暴力解法能拿一分是一分。平台方面目前新系统支持的语言以C、Java、Python为主C、Go有时候也能选但建议用自己最熟练的。我个人推荐Python因为写起来快字符串处理和数据结构都方便尤其是考试时时间紧代码量越少越不容易出低级语法错误。不过要注意华为机试部分老题目存在输入读取的细节坑比如行尾空格、空行等Python里面统一用sys.stdin.readline().strip()处理会稳很多。另外考试系统是单组输入居多但遇到循环输入到文件结束的场景要用while True try处理这个后面第4节会详细讲。还有一点容易被忽略机试不是只考你会不会写代码它考的是你在限定时间内读懂题目、选对算法、写出无bug代码的综合能力。所以刷模拟题的时候我建议你严格按2小时来卡表别一道题磨一上午。状态是要练出来的平时不模拟真考上了考场大概率会手忙脚乱。2. 第6套模拟题的选题思路与考点地图这套模拟题我设计的时候参考了最近华为机试的高频考点分布。从整体趋势看字符串处理是永远的大头几乎每套题都至少有一道动态规划考得也很频繁但不会出特别偏的题经典模型就够用第三题则越来越倾向于实际业务场景比如任务调度、资源分配这背后其实就是拓扑排序、贪心、优先队列这些数据结构的组合应用。本套三道题的安排如下题号分值题目核心考点难度第1题100压缩字符串展开字符串解析、简单遍历低第2题100最大连续子数组和动态规划/贪心中第3题200智能任务调度拓扑排序优先队列高选这三道题的理由很实在。压缩字符串展开是华为机试的常客类似题目换着花样出本质是考字符串的逐字符解析能力属于那种会的人一眼看穿不会的人绕半天的题。最大连续子数组和是动态规划里最经典的入门模型代码量很小但能考出你对状态转移的理解程度因为这道题可以有暴力、前缀和、Kadane算法三种写法。至于任务调度则是把依赖关系和优先级结合起来非常接近真实工作中有依赖的任务怎么排优先级的场景也是200分题里比较典型的出题方向。我建议你把这三道题当成一套完整的模拟卷来做而不是拆开单独刷。按考试要求先做第1题再做第2题最后攻坚第3题。第1题要保证一次性通过所有用例第2题控制在30分钟内完成第3题如果40分钟内没有完整思路果断先写暴力版拿部分分然后继续优化。这样练出来的节奏感比单纯刷题量重要得多。3. 逐题精讲从读题到AC的完整思路3.1 第1题压缩字符串展开题目大意给一个压缩后的字符串规则是字母数字的组合连续出现几次就重复几次例如a5b3c1展开后是aaaaabbbbc。数字可能是多位数比如a12b2展开为12个a加2个b。输入字符串只包含大小写字母和数字要求输出展开后的结果。这道题没有太多陷阱核心就是一次遍历。遇到字母时记录下来然后开始收集后续的数字字符直到遇到下一个字母为止。这里有个关键点数字可能是多位数所以不能只读一位要用while循环把连续的数字全部读进来再转成整数。写代码时还要注意字符串结尾可能恰好是数字不要越界。参考代码s input().strip() res [] i 0 n len(s) while i n: if s[i].isalpha(): ch s[i] i 1 num_start i while i n and s[i].isdigit(): i 1 count int(s[num_start:i]) res.append(ch * count) else: # 理论上输入格式正确不会走到这里但防御一手 i 1 print(.join(res))为什么要用列表收集而不是直接字符串拼接原因是Python字符串是不可变对象每次res ch * count都会创建新字符串当展开结果很长的时候效率会明显下降。用列表最后join一次既省内存又省时间。这个习惯在第二题和第三题里同样适用。时间复杂度O(n)n是展开后字符串的长度空间复杂度同理因为结果本身就要存下来。这道题的易错点有两个。第一忘记处理多位数只取了一位用例里一旦出现10以上次数就直接挂。第二没有注意输入末尾是数字的情况循环里读数字读到末尾时i n如果代码里还尝试访问s[i]就会越界。上面代码用while条件同时判断i n就是为了一并解决这两个问题。3.2 第2题最大连续子数组和题目大意给定一个整数数组可能包含负数求一个连续子数组使得它的和最大输出这个最大和。例如输入[-2,1,-3,4,-1,2,1,-5,4]最大连续子数组是[4,-1,2,1]和为6。这道题是动态规划里的经典模型很多人在LeetCode上见过但华为机试喜欢把它藏在各种业务场景里考比如连续n天股价最大涨幅连续登录的最长收益等本质都是同一个问题。我建议你不仅要会写还要能讲清楚状态转移为什么成立。核心思路是遍历数组时维护两个变量cur表示以当前元素结尾的连续子数组的最大和max_sum表示到目前为止全局的最大和。对于每个元素xcur只有两种选择要么把x接到前面的子数组后面即cur x这样能利用之前的累计和要么抛弃前面所有元素从x重新开始即x本身。两者取较大值就是新的cur。全局最大和则是所有cur里最大的那个。参考代码n int(input()) arr list(map(int, input().split())) if not arr: print(0) exit() max_sum cur arr[0] for x in arr[1:]: cur max(x, cur x) max_sum max(max_sum, cur) print(max_sum)这里有一个初学者容易踩的坑数组全是负数的时候很多人会把初始cur设成0结果全部负数时答案变成0但题目要求子数组不能为空所以正确答案应该是最大的那个负数。解决办法就是初始值直接取arr[0]从第二个元素开始遍历这样即使全负数也能得到正确结果。其实这道题还有前缀和优化的写法不过Kadane算法已经是时间复杂度O(n)、空间复杂度O(1)的最优解考试时直接写这个就够了。面试或技术面如果有人延伸问你如果数组可以循环最大子数组和怎么求那就是另一个问题了核心思路是分两种情况要么最大和子数组不跨越边界直接用Kadane要么跨越边界等价于用总和减去最小子数组和。这类变体建议你私下推一遍因为华为机试第三题如果出难很可能就是在经典模型上套一个环或套一个约束。3.3 第3题智能任务调度题目大意有n个任务每个任务有一个编号id、执行时间time和优先级prio优先级数字越小越优先。任务之间可能存在依赖关系给定m行依赖关系每行a b表示任务a必须在任务b之前完成。现在要求输出一个合理的调度顺序在满足所有依赖关系的前提下优先级高的任务优先输出如果优先级相同编号小的先输出。如果依赖关系导致无法满足比如存在环输出-1。这道题是典型的拓扑排序优先队列组合。拓扑排序用来处理依赖关系优先队列用来在每一时刻从所有当前无依赖的任务中选出优先级最高的那个。如果你只想到用普通队列做拓扑排序那只能保证依赖顺序无法满足优先级的要求反过来如果只用优先队列而不管依赖又会输出错误顺序。两个知识点必须结合。先说整体框架统计每个任务的入度有多少前置任务同时建图记录当前任务完成后哪些任务可以解锁。入度为0的任务就是当前可以调度的候选集合把它们全部丢进优先队列。每次从优先队列里弹出优先级最高、编号最小的任务加入结果列表然后把它的所有后继任务的入度减1如果某个后继任务入度变成0就继续丢进优先队列。重复这个过程直到队列为空。参考代码import heapq n int(input()) tasks {} for _ in range(n): tid, time, prio map(int, input().split()) tasks[tid] {time: time, prio: prio} m int(input()) graph {tid: [] for tid in tasks} indeg {tid: 0 for tid in tasks} for _ in range(m): a, b map(int, input().split()) graph[a].append(b) indeg[b] 1 heap [] for tid in tasks: if indeg[tid] 0: heapq.heappush(heap, (tasks[tid][prio], tid)) order [] while heap: prio, tid heapq.heappop(heap) order.append(tid) for nxt in graph[tid]: indeg[nxt] - 1 if indeg[nxt] 0: heapq.heappush(heap, (tasks[nxt][prio], nxt)) if len(order) ! n: print(-1) else: print( .join(map(str, order)))这里我解释几个关键决策。第一优先队列里存的是(prio, tid)元组Python的heapq会先按第一个元素排序再按第二个元素排序正好满足优先级数字越小越优先相同则编号小优先的要求。第二为什么入度减到0才入队而不是刚把前置任务弹出就入队因为一个任务可能有多个前置任务必须等所有前置都完成它的入度才会变成0。第三最后判断len(order) ! n就是用来检测环的如果存在环环上的任务入度永远无法变成0最终不会进入队列排序结果必然少于n个任务。关于执行时间这道题我在题目里给了这个字段但暂时没用到因为一旦加入执行时间问题就从输出一个满足优先级和依赖的顺序升级为如何调度使得总完成时间最短那需要再引入关键路径或动态规划复杂度会上一个台阶。华为的一些高难度题目确实会这么考所以建议你先把这道基础版吃透然后思考一下如果题目的目标改成计算所有任务完成的最短时间你会怎么改代码思路是维护每个任务的最早开始时间取所有前置任务完成时间的最大值最后再求全局最大值本质上是拓扑排序DP。4. 考场实战这些不刷题永远踩不到的坑第6套模拟题做完了我们聊聊考场上的实际问题。很多人在家里刷LeetCode很顺一上华为机试就挂问题往往不在算法本身而在输入输出和考试环境上。我按经验列几个高频坑。第一个坑是输入读取。华为机试的输入格式经常是第一行一个整数n第二行n个整数但如果第二行的数字间有多个空格、或者末尾多了一个空格直接split()其实没问题Python会智能处理。真正危险的是行首或行尾有多余的空行尤其用input()读的时候容易漏读空行导致EOFError。稳妥做法是统一用sys.stdin.readline()或者input().strip()去掉换行和空格。如果是循环读取多组测试直到EOF必须用while True: try: line input() except EOFError: break否则最后一组数据会报错。第二个坑是输出格式。题目要求输出一行数字数字间用空格分隔末尾不能有多余空格。很多人直接用print(order)结果输出[1, 2, 3]百分百判错。正确做法是 .join(map(str, order))。如果要求最后没有换行其实在线判题一般不太计较结尾换行但你要是用了sys.stdout.write就需要注意自己补换行。第三个坑是时间复杂度预判。第3题任务调度如果你用的是每次遍历找最大优先级来模拟调度当任务数达到10^4、依赖关系达到10^5时O(n²)会直接超时用例只能过一小部分。提前选对数据结构比现场debug要重要得多。这里有个经验法则题目给的n在10^5级别你的算法复杂度要是O(n²)基本就没希望全过了得想到O(nlogn)或者O(n)。第四个坑是双机位的设备测试。这个问题和代码无关但影响巨大。考试开始前系统会让你做环境检测重点检查摄像头、麦克风、屏幕共享权限。我用的是Chrome浏览器权限设置在地址栏左侧那个小图标里一定要在考试系统里实际点一次开启摄像头看画面是否正常。还有考试过程中副机位需要保持屏幕常亮手机别锁屏如果手机息屏时间设置太短中途黑屏可能会被判定异常。我个人建议考试前把手机自动锁屏设为永不然后插上充电器。第五个坑是时间分配。2小时做3题很多人的错误是死磕第3题。我的建议是第1题15分钟内必须AC第2题最多40分钟剩下的1小时全留给第3题。如果第3题30分钟还没有完整思路立刻写暴力枚举版本先保证能过用例的30%到50%然后再慢慢优化。因为华为机试按用例给分暴力版本不是零分只有空着才是零分。5. 从第6套延伸开刷题路线和学习方法如果你把这套题全部吃透了接下来该做什么我建议按下面的路线继续推进。第一优先级是Python内置函数的熟练度比如join、split、sorted、heapq、collections.deque、defaultdict这些在机试里使用频率极高能省你大量时间。第二优先级是算法模板的背诵包括拓扑排序、BFS/DFS、Dijkstra、并查集、滑动窗口、二分查找、经典DP背包、最长递增子序列、编辑距离每类题至少手写5遍达到不看代码也能默写的程度。第三优先级是实战模拟每周至少完整做2套模拟题卡时间、录屏、复盘找出自己哪类题最慢、哪类题最容易错。还有一个很重要的学习方法建自己的错题本但不要只抄题目和解法要写下当时为什么会卡住。比如做第3题时如果你想的是先用DFS枚举所有拓扑序再挑一个满足优先级的说明你没有意识到拓扑排序天然就是处理依赖的方式如果你想到了拓扑排序但没想到优先队列说明你对数据结构的组合应用还不够熟练。把这种思维卡点写下来比写十遍正确答案都管用。至于刷题量网上有说法是刷200题稳过刷500题可以冲高分但我觉得更重要的是覆盖度。华为机试的题库非常庞大但知识点是有限的。你与其在LeetCode上随机刷500道不如按考点分类来每类刷20道左右把常见题型和变体都见一遍。这套第6套模拟题里出现的三类考点就是我筛选出来的高频方向先把这类题练到稳定AC再扩展到图论和更复杂的DP。我在实际备考过程中还有一个习惯每次做完模拟题会自己给自己出一个变体题。比如做完第2题最大连续子数组和我就想如果要求输出最大和对应的子数组区间怎么办然后试着改代码做完第3题任务调度我就想如果把优先级改成执行时间最短优先怎么办。这样一顿操作下来虽然只做了三道题但相当于练了六到八道而且思维深度完全不一样。这个方法我推荐给所有备考的人。最后再分享一个小技巧。华为机试的判题系统有时候会提示运行超时或者内存超限但不会告诉你哪个用例挂了。这时候你可以自己写一个暴力版本和小规模随机数据对拍。我备考时写了一个简单的数据生成器不断生成随机数组分别跑两份代码一旦结果不一致就能定位到逻辑漏洞。这个方法帮我抓出了不少隐藏bug实用性极高。
返回列表