写在前面
本次给大家带来2026年8月19日华为非AI方向笔试题的3道题,涉及到的岗位是:通软,嵌软,测试,普通算法(岗位名中不包含AI)以及数据科学,考试内容和报考部门无关,只和岗位有关。
需要我整理的机考一周速成题单可访问文章底部左侧:阅读原文
第1题-迷宫逃脱(150分)
题目内容
在一个 的网格迷宫中,有一位探险者被困其中,需要找到一条路径,从起点逃到出口。
迷宫中的每个格子可能是以下元素之一:
:空地,可以自由通行 :墙壁,无法通行 :陷阱,可以踏过,但每踩一次会损失 点生命值 :起点(全图仅有一个) :出口(全图仅有一个) :生命药水,拾取后生命值恢复至满值
探险者初始拥有 点生命值。生命值一旦降为 ,探险者立即倒下,无法继续移动。探险者每次只能向上、下、左、右四个方向移动到相邻格子,每移动一步,步数加 。
任务:求出探险者从起点到达出口所需的最少步数。若无法到达出口,或在途中因生命值耗尽而倒下,则输出 。
输入格式
第一行包含三个整数 ,其中 ,。
接下来 行,每行包含 个整数,表示迷宫网格。
输出格式
输出一个整数,表示从起点到出口的最少步数;若无法逃脱,则输出 。
样例 1
输入
4 5 33 0 0 0 00 1 0 1 00 2 0 2 00 0 0 0 4输出
7说明
沿上方与右方的墙边行走,共需 步。
解题思路
机器人到达同一个位置时,剩余生命值可能不同,而剩余生命值会直接影响后续能否继续经过陷阱。因此,不能只记录机器人当前所在的坐标,还需要把当前生命值一起作为状态。
定义状态:
其中:
表示机器人当前所在位置; 表示机器人当前剩余生命值,满足 。
因为每次移动的步数都增加 ,所有边的代价完全相同,所以可以使用 BFS 求最少步数。
从起点开始,初始状态为:
对于当前状态 ,枚举上下左右四个相邻位置 :
如果越界或者目标位置是墙壁 1,不能移动。如果目标位置是普通空地 0、起点3或出口4,生命值不变。如果目标位置是陷阱 2,生命值减少 :如果 ,机器人损毁,该状态不能加入 BFS。如果目标位置是生命药水 5,生命值直接恢复为满生命:
使用三维数组 vis[x][y][h] 记录状态是否已经访问。
需要特别注意:即使两个状态处于同一个格子,只要剩余生命值不同,就不能简单地认为它们是同一个状态。例如,剩余 点生命和剩余 点生命到达同一个位置,后者可能还能继续经过陷阱,而前者可能无法通过。因此必须分别记录。
BFS 按照步数从小到大扩展状态,所以第一次到达出口时,对应的步数一定是最少步数,可以直接返回。
如果 BFS 结束后仍然没有到达出口,则返回 -1。
代码实现
python代码(C++和JAVA代码见平台)
from collections import dequedefescape(a, m, n, k): sx = sy = -1# 找到唯一的起点for i in range(m):for j in range(n):if a[i][j] == 3: sx, sy = i, j# vis[x][y][h] 表示到达 (x, y) 且剩余 h 点生命的状态是否访问过 vis = [[[False] * (k + 1) for _ in range(n)] for _ in range(m)]# 队列元素依次表示:行、列、剩余生命值、已经走的步数 q = deque() q.append((sx, sy, k, 0)) vis[sx][sy][k] = True dx = [-1, 1, 0, 0] dy = [0, 0, -1, 1]while q: x, y, hp, step = q.popleft()# BFS 第一次到达出口时,当前步数一定最少if a[x][y] == 4:return stepfor d in range(4): nx = x + dx[d] ny = y + dy[d]# 越界不能移动if nx < 0or nx >= m or ny < 0or ny >= n:continue# 墙壁不能通过if a[nx][ny] == 1:continue nh = hp# 进入陷阱后损失 1 点生命if a[nx][ny] == 2: nh -= 1if nh == 0:continue# 进入生命药水格后恢复满生命elif a[nx][ny] == 5: nh = k# 同一个位置、同一种剩余生命值只需要处理一次ifnot vis[nx][ny][nh]: vis[nx][ny][nh] = True q.append((nx, ny, nh, step + 1))return-1defmain(): m, n, k = map(int, input().split()) a = []for _ in range(m): row = input().split()# 兼容样例中的 30000 形式if len(row) == 1and len(row[0]) == n: a.append([int(c) for c in row[0]])else: a.append(list(map(int, row))) ans = escape(a, m, n, k) print(ans)if __name__ == "__main__": main()第2题-网络数据流的有效传输段分析(150分)
题目内容
在物理层或数据链路层的通信协议中,数据流通常由一系列字节组成。为了保证数据能够准确传输,协议定义了用于标识数据边界的同步字符,以及真正需要传输的负载数据。由于信道不稳定,数据流中可能夹杂着干扰信号。
接收端需要从原始数据流中识别出符合特定规则的有效传输段,以便进行后续校验。
定义
同步字符:由任意大写字母(A‑Z)组成,用于界定一个有效传输段的开始和结束。 干扰字符:由任意小写字母(a‑z)组成。 有效字符:除干扰字符外,其余字符均为有效字符。 干扰度:一个有效传输段中间部分包含的干扰字符(即小写字母)的数量。 有效传输段:数据流中一个连续的子串,必须以同步字符开头、以同步字符结尾,且两端同步字符之外先出现有效字符。
任务
给定一个目标干扰度 limit 和一段接收到的数据流字符串 stream,请找出干扰度恰好等于 limit 的最长有效传输段,并输出其长度;如果找不到满足条件的传输段,则输出 0。
输入描述
第一行输入一个整数 limit,表示目标干扰度,取值范围为 。
第二行输入一个字符串 stream,表示接收到的原始数据流,由大小写字母和数字组成,长度范围为 。
输出描述
输出一个整数,表示满足条件的最长有效传输段的长度。
样例 1
输入
0YsdbYYYdYvXYXfgh输出
3说明
满足条件的字符串有两个,分别为 YYY 和 XYX,长度为 。
样例 2
输入
22YCA输出
0说明
没有满足条件的字符串,输出 。
解题思路
核心思路
有效传输段需要满足:
子串的第一个字符是大写字母; 子串的最后一个字符是大写字母; 子串中的小写字母数量恰好为 limit;数字属于有效字符,但不会增加干扰度; 中间出现大写字母不会影响有效性,例如样例中的 YYY、XXX都是合法的有效传输段。
因此,可以利用前缀计数来快速判断两个大写字母之间包含多少个小写字母。
从左到右遍历字符串,用 cnt 表示当前位置之前以及当前位置累计出现的小写字母数量。
对于一个位于下标 i 的大写字母:
当前累计小写字母数量为 cnt;假设有效传输段从之前某个大写字母下标 j开始;由于 i和j都是大写字母,所以区间[j,i]中的小写字母数量为
希望干扰度恰好为 limit,因此需要满足
即
所以,对于每一个可能的累计小写字母数量,只需要记录第一次出现该计数时对应的大写字母位置。
当遍历到当前大写字母时,查找累计计数为 cnt-limit 的最早大写字母位置。如果存在,就可以组成一个干扰度恰好为 limit 的有效传输段。
为了使长度最大,相同累计计数下只记录最早的大写字母位置,因为起点越靠前,得到的有效传输段越长。
实现方法
初始化
cnt = 0,表示当前累计的小写字母数量。使用数组
first:first[x]表示累计出现x个小写字母时,最早出现的大写字母下标;初始值统一设为 -1。从左到右遍历
stream:计算 need = cnt - limit;如果 need >= 0且first[need]已经存在,则当前大写字母可以作为结尾,更新最大长度;如果 first[cnt]尚未记录,则记录当前大写字母的位置。如果当前字符是小写字母,则
cnt++;如果当前字符是大写字母:
数字既不增加
cnt,也不能作为有效传输段的边界,因此无需额外处理。遍历结束后输出最大长度。
代码实现
python代码(C++和JAVA代码见平台)
defget_max(limit, stream): n = len(stream)# first[x] 表示累计出现 x 个小写字母时,# 最早出现的大写字母下标 first = [-1] * (n + 1) cnt = 0 ans = 0for i in range(n): ch = stream[i]# 小写字母会增加干扰度if'a' <= ch <= 'z': cnt += 1# 只有大写字母才能作为有效传输段的边界elif'A' <= ch <= 'Z': need = cnt - limit# 找到累计小写字母数为 need 的最早大写字母,# 则二者之间恰好包含 limit 个小写字母if need >= 0and first[need] != -1: length = i - first[need] + 1if length > ans: ans = length# 相同累计计数只保留最早的大写字母位置,# 这样才能使之后得到的区间尽可能长if first[cnt] == -1: first[cnt] = i# 当前大写字母本身也可以构成干扰度为 0 的传输段if limit == 0and ans == 0: ans = 1return ansdefmain(): limit = int(input()) stream = input().strip() print(get_max(limit, stream))if __name__ == "__main__": main()第3题-流量均衡控制(150分)
题目内容
通信设备厂商生产的交换机需要对用户的信息进行处理:按照信息的类别划分优先级,并为每种流量类型设置权重,用于在同优先级的场景下,让设备优先处理权重更高的消息。具体分类如下:
假设某台交换机当前由 2 个端口组成一组,共同负责消息的接收与处理。这两个端口需要处理的所有消息的权重构成数组 arr。请问是否存在一种消息分发方式,使得每个端口处理的消息的权重均值相同,即能否将权重数组拆分为两个均值相等的子数组?本题只要求判断是否存在这样的分组,无需考虑多种分组方式。
输入描述
输入为一个字符串,每个数字代表一条消息的权重。权重的取值只能来自上表,即 ,不存在其他值。各权重之间用空格分隔,例如:
5 5 5 20 15 15 5 5 20 15 15 5 5 20 15 15 15数组大小(即消息数量)满足 。
输出描述
第一行:输出 0 或 1。1 表示存在一种消息分发方式,使得各端口处理的消息权重均值相同;0 表示不存在这样的分发方式。
第二行:若第一行输出为 1,则输出其中一个子数组的元素和;若两个子数组的元素和不同,则输出较小的那一个。若第一行输出为 0,则本行无需输出。
样例 1
输入
15 20输出
0说明
不存在任何划分方式,能使两个子数组的均值相同,因此输出 0。
样例 2
输入
5 20 15 15 5 5 5 20 5 5 15 15输出
165说明
每个端口处理的消息权重可以取 5 5 5 20 15 15,此时两个端口处理的消息权重均值一致,均为 。
解题思路
核心思路
设原数组共有 个消息,总权重和为 。
假设其中一个端口分到 个消息,这 个消息的权重和为 ,那么另一个端口有 个消息,权重和为 。
要求两个端口处理消息的权重均值相同,因此有:
交叉相乘:
整理得到:
因此,对于某个分组大小 ,它的元素和必须恰好为:
于是问题转化为:
在数组中,是否能够选择 $1\le k<n$ 个元素,使这些元素的和恰好为="" $\frac{ks}{n}$。<="" p="">
可以使用二维动态规划:
dp[k][s] 表示是否能够从已经处理的元素中选出恰好 个,使权重和为 。
初始状态:
遍历每个权重 时,倒序枚举元素个数 和权重和 ,进行转移:
因为题目中的权重只有 0、5、15、20,全部都是 的倍数,可以先全部除以 ,变成:
0、1、3、4
这样最大权重和由 降低为 ,可以明显缩小动态规划状态范围。
DP 完成后枚举 :
如果 不能被 整除,则该 不可能满足要求。 否则令 。 如果 dp[k][x]为真,则存在合法划分。两组元素和分别为 和 ,输出其中较小的一个即可。
整个过程只进行整数运算,不需要计算浮点平均值,因此不存在精度问题。
需要注意,以上算法严格按照题目中“两个分组的算术平均值相等”这一条件实现。题面给出的样例 2、样例 3 与该数学定义存在明显矛盾:例如样例 3 中 [15] 的均值为 ,而 [20,20] 的均值为 ,两者并不相等。若原题确实要求算术平均值相等,则应以公式 为准。
实现方法
将所有权重除以 ,缩小状态空间。
计算缩放后的总权重和
sum。建立
dp[n + 1][sum + 1]。对每个消息权重进行 0-1 背包更新:
元素数量必须倒序枚举,防止一个消息被重复选取。 权重和同样倒序枚举。 枚举一个端口可能取得的消息数量
k:判断 k * sum是否可以整除n。得到目标和 target。判断 dp[k][target]是否存在。若存在,返回两个分组中较小的权重和,并乘回 。
如果所有
k都无法满足,则返回0。
代码实现
python代码(C++和JAVA代码见在线平台)
defsolve(arr): n = len(arr)# 所有权重都是 5 的倍数,先除以 5 缩小动态规划范围 a = [x // 5for x in arr] total = sum(a)# dp[k][s] 表示能否选出恰好 k 个元素,使元素和为 s dp = [[False] * (total + 1) for _ in range(n + 1)] dp[0][0] = True cnt = 0 cur = 0for v in a: cnt += 1 cur += v# 倒序枚举元素数量,保证每个消息只能选择一次for k in range(cnt, 0, -1):# 倒序枚举当前可能达到的权重和for s in range(cur, v - 1, -1):if dp[k - 1][s - v]: dp[k][s] = True# 枚举其中一个端口处理的消息数量for k in range(1, n):# target = k * total / n 必须是整数if k * total % n != 0:continue target = k * total // nif dp[k][target]:# 两组元素和分别为 target 和 total - target ans = min(target, total - target) * 5return1, ansreturn0, 0if __name__ == "__main__": arr = list(map(int, input().split())) ok, ans = solve(arr) print(ok)if ok == 1: print(ans)刷题练习:CodeFun2000.com
夜雨聆风