大厂真题 / 华为
华为 6.3 笔试真题 - 研发岗(非AI方向)
本场考试概述
考试时间:2026年6月3日 考试岗位:研发岗(通软、嵌软、测试、普通算法、数据科学等非AI方向) 难度评级:中等偏难
考点分析:
- 第一题:数字标识符压缩算法——模拟(难度中等)
- 第二题:爆破小游戏——树上DFS(难度中等)
- 第三题:昇腾NPU协同调度系统——回溯搜索 + 状态压缩(难度困难)
建议策略:
- 前两题的字符串模拟和树形搜索是华为研发岗高频考点,务必稳拿
- 第一题关键是规则顺序——先合并连续相同数字组再去前导零,判断相同数字组时基于原始 4 位分组
- 第三题状态压缩 + 回溯偏难,时间紧张时先保证前两题正确,再攻坚第三题
第 1 题:数字标识符压缩算法
题目描述
标准数字标识符由 32 位组成,表示为 8 组 4 位小写十六进制数,用冒号分隔。按以下三条规则压缩:
- 规则一(前导零压缩):每组去掉前导零,如
0db8→db8,0000→0 - 规则二(连续相同数字组压缩):若干个连续、彼此完全相同、且该组 4 位字符也完全相同的组(如
0000、aaaa)可合并为**,整个标识符中只能使用一次。取最长段,长度相同取最左 - 规则三(优先级):先应用规则二,再应用规则一
样例
输入
2001:0db8:85a3:0010:0001:8a2e:0370:7334
输出
2001:db8:85a3:10:1:8a2e:370:7334
思路分析
第一步:观察题目性质
组数固定为 8,每组固定 4 个字符,直接模拟即可。关键是规则的执行顺序:必须在原始 4 位分组上完成连续相同段的查找,否则去掉前导零后 0000 变成 0,长度改变就无法正确判断”4 位字符完全相同”。
第二步:判定”相同数字组”
一个组只有当 4 位字符完全相同(如 aaaa、0000)时才可能参与 ** 合并。
第三步:扫描最长连续段
从左到右遍历,遇到相同数字组时向右延伸统计连续段长度。维护最长段的起点和长度,只在严格更长时更新——并列时自动保留最左一段。
第四步:重建结果
走到最长段起点时输出 ** 并跳过整段,其余组去前导零后输出,用冒号拼接。
题解代码
import sys
input = sys.stdin.readline
def solve():
s = input().strip()
groups = s.split(":")
best_start = -1
best_len = 0
i = 0
while i < len(groups):
g = groups[i]
if len(g) == 4 and g[0] == g[1] == g[2] == g[3]:
j = i + 1
while j < len(groups) and groups[j] == groups[i]:
j += 1
if j - i > best_len:
best_len = j - i
best_start = i
i = j
else:
i += 1
result = []
i = 0
while i < len(groups):
if i == best_start:
result.append("**")
i += best_len
else:
result.append(groups[i].lstrip('0') or '0')
i += 1
print(":".join(result))
solve()
复杂度分析
时间复杂度:$O(n \cdot k)$,$n = 8$ 组,$k = 4$ 字符,常数级 空间复杂度:$O(n)$,存储分组与结果
第 2 题:爆破小游戏
题目描述
游戏地图是一棵带边权的树,每个节点有收益值。炸弹放在某个节点上,影响范围为 $k$:到炸弹所在节点的距离(路径上边权之和)不超过 $k$ 的节点全部被爆破,获得收益之和。选择最优放置点使收益最大。
$n \leq 1000$。
样例
输入
7 3
10 10 10 300 10 10 300
0 1 1
0 2 1
1 3 3
1 4 1
2 5 3
2 6 1
输出
640
思路分析
第一步:观察题目性质
树上任意两点路径唯一,所以从放置点出发做 DFS,沿途累加距离,只要不超过 $k$ 就收集收益。$n \leq 1000$,枚举每个节点做一次 DFS 总复杂度 $O(n^2)$,完全可以接受。
第二步:距离受限的 DFS
从放置点出发递归,带上到当前节点的累计距离 $d$。若 $d > k$ 直接返回 0(剪枝——边权非负,子树只会更远)。否则累加当前节点收益并递归所有子节点。
第三步:取全局最大值
枚举所有 $n$ 个节点作为放置点,取最大收益。
以样例为例,炸弹放在节点 2:
- 节点 2 自身距离 0 ≤ 3,收益 10
- 节点 0 距离 1 ≤ 3,收益 10
- 节点 1 距离 2 ≤ 3,收益 10
- 节点 6 距离 1 ≤ 3,收益 300
- 节点 4 距离 3 ≤ 3,收益 10
- 节点 5 距离 3 ≤ 3,收益 10(实际样例放节点2只得部分)
放在节点 1 时:节点 1(10) + 节点 0(10, 距离1) + 节点 2(10, 距离2) + 节点 4(10, 距离1) + 节点 6(300, 距离3) + 节点 3(300, 距离3) = 640。
题解代码
import sys
from sys import setrecursionlimit
setrecursionlimit(100000)
input = sys.stdin.readline
def solve():
n, k = map(int, input().split())
g = list(map(int, input().split()))
tree = [[] for _ in range(n)]
for _ in range(n - 1):
u, v, w = map(int, input().split())
tree[u].append((v, w))
tree[v].append((u, w))
def dfs(u, parent, dist):
if dist > k:
return 0
total = g[u]
for v, w in tree[u]:
if v != parent:
total += dfs(v, u, dist + w)
return total
ans = 0
for s in range(n):
ans = max(ans, dfs(s, -1, 0))
print(ans)
solve()
复杂度分析
时间复杂度:$O(n^2)$,枚举 $n$ 个放置点,每次 DFS 最多访问 $n$ 个节点 空间复杂度:$O(n)$,邻接表与递归栈
第 3 题:昇腾NPU协同调度系统
题目描述
管理昇腾 NPU 上的训练任务。每个任务有三个属性:需要占用的 NPU 卡编号列表、执行时间片数、上游依赖(至多 1 个,-1 表示无依赖)。
调度规则:
- 每张卡同一时间只能执行一个任务
- 任务必须在上游完成后才能开始
- 任务一旦开始不能被抢占
- 求所有任务全部完成的最短总耗时
任务数 $\leq 10$,NPU 卡数 $\leq 10$。
样例
输入
2 3
0
3
-1
0
2
-1
1
4
1
输出
6
思路分析
第一步:观察题目性质
任务之间会抢同一张卡、还有先后依赖,不存在一种固定排法总是最优。但任务数 $\leq 10$,所有执行顺序最多 $10!$ 种,可以暴力枚举所有合法顺序取最优。
第二步:贪心确定开始时间
对于一个固定的调度顺序,每个任务都”能多早就多早”开始一定是最优的。原因是:任务一旦开始不能中断、时长固定,让某任务提前开始只会让它占的卡更早空出来,绝不会让任何后续任务变晚。
所以对固定顺序,每个任务的开始时间 = $\max(\text{上游完成时间}, \text{所需各卡的最早可用时间})$。
第三步:回溯枚举 + 剪枝
用位掩码表示已安排的任务集合,逐个挑能开工的任务往后放。每多排一个任务,总耗时只增不减——一旦当前耗时已追平已知最优解,直接剪掉该分支。
第四步:位掩码存储所需卡
把任务所需的卡编号列表压成一个整数(位掩码),用位运算快速判断某张卡是否被占用。
题解代码
import sys
input = sys.stdin.readline
def solve():
card_cnt, task_cnt = map(int, input().split())
need = []
dur = []
pre = []
for i in range(task_cnt):
cards = list(map(int, input().split()))
mask = 0
for c in cards:
mask |= (1 << c)
need.append(mask)
dur.append(int(input()))
pre.append(int(input()))
free_at = [0] * card_cnt
finish = [0] * task_cnt
best = [float('inf')]
def dfs(done, cur_time):
if done == (1 << task_cnt) - 1:
best[0] = min(best[0], cur_time)
return
if cur_time >= best[0]:
return
for i in range(task_cnt):
if done & (1 << i):
continue
if pre[i] != -1 and not (done & (1 << pre[i])):
continue
start = finish[pre[i]] if pre[i] != -1 else 0
for c in range(card_cnt):
if need[i] & (1 << c):
start = max(start, free_at[c])
end = start + dur[i]
new_time = max(cur_time, end)
if new_time >= best[0]:
continue
old_free = free_at[:]
old_finish = finish[i]
for c in range(card_cnt):
if need[i] & (1 << c):
free_at[c] = end
finish[i] = end
dfs(done | (1 << i), new_time)
for c in range(card_cnt):
free_at[c] = old_free[c]
finish[i] = old_finish
dfs(0, 0)
print(best[0])
solve()
复杂度分析
时间复杂度:最坏 $O(m! \cdot c)$,$m$ 为任务数,$c$ 为卡数。$m \leq 10$,加上剪枝实际远小于上界 空间复杂度:$O(m + c)$,存任务完成时刻、卡可用时刻和递归栈
小结
- 第一题是模拟题,关键是规则优先级:先在原始 4 位分组上找最长连续相同段合并为
**,再去前导零 - 第二题是树上 DFS,$n \leq 1000$ 允许枚举每个放置点各做一次距离受限的 DFS,注意边权非负的剪枝
- 第三题是回溯 + 状态压缩,任务数 $\leq 10$ 决定了可以暴力枚举所有调度顺序,核心优化是”当前耗时已不可能更优则剪枝”