华为笔试 华为笔试题 华为0318 春招实习笔试真题
笔试时间:2026年3月18日
往年笔试合集:
第一题:大模型训练显存优化算法
题目
在大模型训练中,为解决NPU显存不够用的情况,通常采用张量swap或者张量重计算的方式进行优化;张量swap是把张量的数据先搬到CPU,需要的时候,再从CPU搬回NPU;张量重计算是在需要张量的值时,在NPU上重新把张量的值计算出来。
假设把模型部署到NPU上,还需要 m 大小的存储空间,才能让大模型运行起来;目前有 n 个候选张量,每个张量占用一定的存储空间;n 个张量中的每个张量都可以进行swap或者重计算,swap和重计算分别对应不同的代价;试着编写一段程序,从 n 个候选张量中选择合适的张量进行swap或者重计算,在代价最小的情况下,使得大模型能够运行起来(选择的张量和大于等于 m)。
输入描述
- 第一行为1个整数
m(0 < m < 10000),表示需要的存储空间大小 - 第二行为1个整数
n(0 < n < 10000),表示候选张量的个数 - 第三行有
n个处于区间 [1, 10000] 之内的整数,表示 n 个候选张量的存储空间大小 - 第四行有
n个处于区间 [1, 100000] 之内的整数,表示 n 个候选张量swap的代价 - 第五行有
n个处于区间 [1, 100000] 之内的整数,表示 n 个候选张量重计算的代价
输出描述
如果没有找到合适的解,输出 error;否则,输出最小代价(行尾没有空格)。
样例输入
10 5 3 4 5 6 7 1 2 3 5 5 2 3 4 5 6
样例输出
6
样例说明
需要的存储空间是10;候选张量长度分别为3,4,5,6,7;张量长度组合 {3,7}、{4,6}、{4,7}、{5,6}、{5,7}、{6,7}、...都大于等于10,满足运行的基本条件;对于 {3,7} 来说,3的最小代价是1,7的最小代价是5,{3,7} 的最小代价是6,经过其他组的比较,达成目标的最小代价是6,所以输出6。
参考题解
解题思路:
本题的本质是一个变种的 0-1 背包问题。我们需要从 n 个张量中选择一部分,使得它们的空间之和 ≥ m,同时要求总代价最小。
- 代价转换:每个张量只能选一种优化方式(swap 或重计算),为了使总代价最小,每个张量应直接取两者的最小值:
cost[i] = min(swap[i], recomp[i])。 - 状态定义:
dp[j]表示腾出至少 j 大小空间所需要的最小代价。 - 目标:求
dp[m]。 - 传统的 0-1 背包通常是"不超过容量 m 的最大价值",而本题是"空间不少于 m 的最小代价"。如果选择的张量总空间 ≥ m,它同样满足运行要求。为了防止 DP 数组过大,我们将所有空间大于等于 m 的状态全部更新到
dp[m]。 - 使用一维数组进行优化,逆序遍历防止重复选择同一张量(0-1 背包特性)。
- 初始化:
dp[0] = 0,其余dp[j] = INF。
C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
int main() {
int m, n;
cin >> m >> n;
vector<int> sizes(n), swaps(n), recomps(n);
for (int i = 0; i < n; i++) cin >> sizes[i];
for (int i = 0; i < n; i++) cin >> swaps[i];
for (int i = 0; i < n; i++) cin >> recomps[i];
// 剪枝:如果所有张量空间加起来不足 m,直接输出 error
long long totalSize = 0;
for (int i = 0; i < n; i++) totalSize += sizes[i];
if (totalSize < m) {
cout << "error" << endl;
return 0;
}
// 每个张量取 swap 和重计算的最小代价
vector<pair<int, int>> items; // (size, cost)
for (int i = 0; i < n; i++) {
int cost = min(swaps[i], recomps[i]);
items.push_back({sizes[i], cost});
}
// 初始化 DP 数组
const int INF = INT_MAX;
vector<int> dp(m + 1, INF);
dp[0] = 0;
int maxJ = 0;
for (auto& [s, c] : items) {
// 0-1 背包逆序遍历
for (int j = maxJ; j >= 0; j--) {
if (dp[j] == INF) continue;
int nj = j + s;
int val = dp[j] + c;
if (nj >= m) {
if (val < dp[m]) dp[m] = val;
} else {
if (val < dp[nj]) dp[nj] = val;
}
}
maxJ += s;
if (maxJ > m) maxJ = m;
}
if (dp[m] == INF) {
cout << "error" << endl;
} else {
cout << dp[m] << endl;
}
return 0;
}
Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int m = sc.nextInt();
int n = sc.nextInt();
int[] sizes = new int[n];
int[] swaps = new int[n];
int[] recomps = new int[n];
for (int i = 0; i < n; i++) sizes[i] = sc.nextInt();
for (int i = 0; i < n; i++) swaps[i] = sc.nextInt();
for (int i = 0; i < n; i++) recomps[i] = sc.nextInt();
// 剪枝:所有张量空间加起来不足 m,直接输出 error
long totalSize = 0;
for (int i = 0; i < n; i++) totalSize += sizes[i];
if (totalSize < m) {
System.out.println("error");
return;
}
// 每个张量取 swap 和重计算的最小代价
int[][] items = new int[n][2];
for (int i = 0; i < n; i++) {
items[i][0] = sizes[i];
items[i][1] = Math.min(swaps[i], recomps[i]);
}
// 初始化 DP 数组
final int INF = Integer.MAX_VALUE;
int[] dp = new int[m + 1];
Arrays.fill(dp, INF);
dp[0] = 0;
int maxJ = 0;
for (int[] item : items) {
int s = item[0], c = item[1];
for (int j = maxJ; j >= 0; j--) {
if (dp[j] == INF) continue;
int nj = j + s;
int val = dp[j] + c;
if (nj >= m) {
if (val < dp[m]) dp[m] = val;
} else {
if (val < dp[nj]) dp[nj] = val;
}
}
maxJ += s;
if (maxJ > m) maxJ = m;
}
if (dp[m] == INF) {
System.out.println("error");
} else {
System.out.println(dp[m]);
}
}
}
Python
import sys
from collections import defaultdict
def solve():
m = int(
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南
