华为笔试 华为笔试题 0827
笔试时间:2025年8月27日
往年笔试合集:
第一题:冬季暖系统温度稳定性分析
某小区宣传其在冬季采暖期间,提供室内温度保持在18 - 24摄氏度之间,且温差在4摄氏度内的恒温服务。为了评价该小区冬季采暖温控效果,现按固定时间间隔对室内温度进行连续N次采样,采样的室内温度按照从0开始升序的索引号依次记录。通过计算采样的室内温度在小区承诺恒温范围内的持续时间,即持续时间内的采样室内温度值T均满足18<= T <= 24 ,且持续时间内的温差(最大温度max - 最低温度min)<=4,持续时间越长,采暖温控效果越好。请输出最大的持续时间开始点和结束点的采样的室内温度索引号,如果存在多个最大持续时间,按持续时间的开始点采样的室内温度索引号升序,逐行输出。
解答要求:
时间限制: C/C++ 1000ms,其他语言: 2000ms
内存限制: C/C++ 256MB,其他语言: 512MB
输入描述
第一行输入为按固定时间间隔对室内温度进行连续采样次N。
第二行输入为按固定时间间隔采集的一段连续的室内温度数值,整型,温度数值取值范围[0, 30]。
输入格式为:用空格分割的温度数值数组输出描述
输出描述
最大持续时间的开始点和结束点的数组元素索引号,中间用空格分隔。存在多组时逐行输出。
说明:数组元素索引号从0开始。
样例输入
6
16 18 20 25 23 20
样例输出
1 2
4 5
解释:满足恒温要求的为数组索引1 - 2、数组索引4 - 5,按起始索引升序输出
参考题解
这道题的思路是使用 滑动窗口 结合 单调队列 来解决。核心思想题目要求找到最长的连续子数组,满足两个条件:子数组中所有温度值都在 [18,24] 之间。子数组中的最大值与最小值的差值不大于 4。由于需要找到最长的子数组,并且要遍历所有可能的子数组,一个高效的方法就是使用滑动窗口。解题步骤定义滑动窗口:我们使用两个指针 L 和 R 来定义一个窗口 [L,R]。R 指针向右移动,不断扩大窗口,而 L 指针在不满足条件时向右移动,缩小窗口。处理温度范围:当 R 指针指向的温度值 T 不在 [18,24] 范围内时,这个 T 肯定不能成为合格子数组的一部分。因此,当前窗口从 L 到 R 都不可能满足条件。我们直接将 L 指针移动到 R+1 的位置,并清空记录窗口内最大最小值的辅助数据结构,重新开始一个新的窗口。处理温差:当 R 指针指向的温度值在 [18,24] 范围内时,我们需要判断当前窗口 [L,R] 是否满足温差 ≤4 的条件。为了快速获取窗口内的最大值和最小值,我们使用两个 单调队列。一个 单调递增队列 记录窗口内最小值的索引(q_min)。队头始终是当前窗口的最小值索引。一个 单调递减队列 记录窗口内最大值的索引(q_max)。队头始终是当前窗口的最大值索引。维护单调队列:每当 R 向右移动,将新的温度值添加到单调队列中。为了保持队列的单调性,需要移除队列尾部不符合单调性要求的元素。为了确保队头元素始终在当前窗口内,当 L 向右移动时,需要检查队头元素的索引是否等于 L。如果是,将其从队列头部移除。更新结果:在每次 R 指针移动后,检查当前窗口 [L,R] 是否满足所有条件(即温度在 [18,24] 且温差 ≤4)。如果温差大于 4,则说明当前窗口不满足条件,需要移动 L 指针来缩小窗口,直到温差再次 ≤4。每次 L 和 R 移动后,如果当前窗口的长度大于之前记录的最大长度,则更新最大长度并清空结果列表,将当前窗口 [L,R] 加入结果。如果当前窗口的长度等于最大长度,则将当前窗口 [L,R] 添加到结果列表中。通过这种方式,我们可以在 O(N) 的时间复杂度内解决问题,因为每个元素最多被 L 和 R 遍历一次,并最多进入和离开单调队列一次。
C++:
#include <iostream> #include <vector> #include <deque> using namespace std; int main() { int n_val; cin >> n_val; vector<int> arr(n_val); for (int i = 0; i < n_val; ++i) { cin >> arr[i]; } int best = 0; vector<pair<int, int>> ans; int L = 0; deque<int> q_min, q_max; for (int R = 0; R < n_val; ++R) { int t = arr[R]; // 如果当前元素不在18-24范围内,重置窗口 if (!(t >= 18 && t <= 24)) { L = R + 1; q_min.clear(); q_max.clear(); continue; } // 维护最大值队列(单调递减) while (!q_max.empty() && arr[q_max.back()] <= t) { q_max.pop_back(); } q_max.push_back(R); // 维护最小值队列(单调递增) while (!q_min.empty() && arr[q_min.back()] >= t) { q_min.pop_back(); } q_min.push_back(R); // 确保当前窗口中最大值与最小值的差不超过4 while (arr[q_max.front()] - arr[q_min.front()] > 4) { if (q_min.front() == L) { q_min.pop_front(); } if (q_max.front() == L) { q_max.pop_front(); } L++; } // 更新最长子数组信息 int cur_len = R - L + 1; if (cur_len > best) { best = cur_len; ans.clear(); ans.emplace_back(L, R); } else if (cur_len == best && best > 0) { if (ans.empty() || ans.back() != make_pair(L, R)) { ans.emplace_back(L, R); } } } // 输出结果 for (const auto& p : ans) { cout << p.first << " " << p.second << endl; } return 0; }
Java:
import java.util.*; import java.io.*; public class RangeSubarrayFinder { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int nVal = Integer.parseInt(br.readLine()); int[] arr = new int[nVal]; StringTokenizer st = new StringTokenizer(br.readLine()); for (int i = 0; i < nVal; i++) { arr[i] = Integer.parseInt(st.nextToken()); } int best = 0; List<int[]> ans = new ArrayList<>(); int L = 0; Deque<Integer> qMin = new ArrayDeque<>(); Deque<Integer> qMax = new ArrayDeque<>(); for (int R = 0; R < nVal; R++) { int t = arr[R]; // 如果当前元素不在18-24范围内,重置窗口 if (!(t >= 18 && t <= 24)) { L = R + 1; qMin.clear(); qMax.clear(); continue; } // 维护最大值队列(单调递减) while (!qMax.isEmpty() && arr[qMax.peekLast()] <= t) { qMax.pollLast(); } qMax.offerLast(R); // 维护最小值队列(单调递增) while (!qMin.isEmpty() && arr[qMin.peekLast()] >= t) { qMin.pollLast(); } qMin.offerLast(R); // 确保当前窗口中最大值与最小值的差不超过4 while (arr[qMax.peekFirst()] - arr[qMin.peekFirst()] > 4) { if (qMin.peekFirst() == L) { qMin.pollFirst(); } if (qMax.peekFirst() == L) { qMax.pollFirst(); } L++; } // 更新最长子数组信息 int curLen = R - L + 1; if (curLen > best) { best = curLen; ans.clear(); ans.add(new int[]{L, R}); } else if (curLen == best && best > 0) { if (ans.isEmpty() || !Arrays.equals(ans.get(ans.size() - 1), new int[]{L, R})) { ans.add(new int[]{L, R}); } } } // 输出结果 for (int[] pair : ans) { System.out.println(pair[0] + " " + pair[1]); } } }
Python:
import sys from collections import deque as dq def main(): n_val = int(sys.stdin.readline()) arr = list(map(int, sys.stdin.readline().split())) best = 0 ans = [] L = 0 q_min = dq() q_max = dq() for R in range(n_val): t = arr[R] ifnot (18 <= t <= 24): L = R + 1 q_min.clear() q_max.clear() continue while q_max and arr[q_max[-1]] <= t: q_max.pop() q_max.append(R) while q_min and arr[q_min[-1]] >= t: q_min.pop() q_min.append(R) while arr[q_max[0]] - arr[q_min[0]] > 4: if q_min[0] == L: q_min.popleft() if q_max[0] == L: q_max.popleft() L += 1 cur_len = R - L + 1 if cur_len > best: best = cur_len ans = [(L, R)] elif cur_len == best and best > 0: ifnot ans or ans[-1] != (L, R): ans.append((L, R)) for a, b in ans: print(a, b) if __name__ == '__main__': main()
第二题:樱桃等级筛选
某大型樱桃加工厂使用自动化机械扫描了一批樱桃的尺寸大小。现在获得了直径范围 [L,H] 各个区间所有的樱桃个数统计。现在需要通过 m 个等级(m < H - L)来筛选不同尺寸大小的樱桃,筛选后需使得各等级内的樱桃数目的标准差最小。
输入描述
第一行输入两个数字,第一个数字表示樱桃的总组数 n(2 < n ≤ 20),第二个数字 m 表示需要获取的等级数目 a,2 < a < n。
第二行输入一个长度为 H - L + 1 的数列 A,a <H - L + 1,其中的第 i 个元素 a_i 表示直径为 L + i 樱桃个数(i ∈ [0,H - L]),0 < a_i < 100。
输出描述
输出长度为 m 的数列 B,
其中的第 1 个元素 b₀表示顺序从 A 中取 b₀个元素,将该尺寸范围内的樱桃作为一个分类等级;
第 2 个元素 b₁表示顺序从 A 中起始点 b₀开始取 b₁个元素,将该尺寸范围内的樱桃作为一个分类等级,依次类推。
样例输入
10 4
16 40 37 20 18 30 18 60 50 37
样例输出
3 3 2 2
参考题解
这道题的核心是组合优化问题,即在所有可能的分类方案中,找到能使标准差最小的那一种。核心思想题目要求将一个长度为 N 的数组分成 M 个连续的子数组,使得这 M 个子数组的和(代表每个等级的樱桃总数)的标准差最小。由于 N 的范围非常小(N≤20),我们可以考虑使用暴力穷举或深度优先搜索(DFS)来遍历所有可能的分割方案。解题步骤确定搜索空间:我们需要将 N 个樱桃尺寸组,分成 M 个连续的等级。一个等级至少包含一个尺寸组。我们可以用一个递归函数(DFS)来尝试所有可能的分割点。递归函数的设计:定义一个 dfs(pos, left, s_list, l_list) 函数。pos: 当前处理的起始位置,即从 A[pos] 开始。left: 还需要分成的等级数。s_list: 存储当前分割方案中,每个等级的樱桃总数。l_list: 存储当前分割方案中,每个等级包含的尺寸组个数。递归的终止条件:当 left 为 0 时,说明我们已经成功将所有樱桃分成了 M 个等级。此时,我们计算 s_list 中所有元素的标准差。如果这个标准差小于我们当前记录的最小值 g_min,则更新 g_min,并记录当前的分割方案 l_list 到 g_res 中。递归的剪枝:为了提高效率,我们可以加入剪枝条件。if N - pos < left: 如果剩余的樱桃组数量小于我们还需要分的等级数,那么无论如何都无法完成分割,直接返回。递归的遍历:在函数内部,我们从当前位置 pos 开始,遍历所有可能的子数组长度,作为当前等级。for j in range(pos, N - left + 1):这个循环控制了当前等级的结束位置 j。对于每一个结束位置 j,我们计算当前等级的樱桃总数,并将其添加到 s_list 中。同时,将当前等级的长度 j - pos + 1 添加到 l_list 中。然后,我们递归调用 dfs(j + 1, left - 1, s_list, l_list),继续处理剩下的樱桃组。递归返回后,我们需要回溯,将
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南