华为OD机考“采样过滤”题解:状态机与多语言实现
2026/9/16 1:29:50 网站建设 项目流程

如果你正在准备华为OD机考,抽到的是C卷,那你对“双机位”三个字应该不陌生——电脑摄像头全程拍脸,手机从侧后方固定机位监考,切屏、后台搜索、查文档都会被记录。这套环境下的答题节奏和平时在自己IDE里写代码完全不同,尤其是“采样过滤”这类看着不难、实则暗藏细节的题,成了很多人在考场上翻车的第一站。

先说一句大实话:华为OD机考是ACM模式,不是力扣那种函数填空,所有输入输出都要你自己用代码处理。很多人刷习惯了核心代码模式,一上机连“数据怎么读进来”都要卡几分钟。“采样过滤”这类题之所以值得单独拿出来讲,就是因为它完美结合了三个高频考点:规则拆解能力、边界处理能力、多语言迁移能力。你无论用Java、Python、JS、C/C++还是Go,核心逻辑都一样,但每个语言都有各自的坑。

这篇文章我会把这道题从题目模型到五种语言的完整实现全部拆开讲,包括参考值更新的两种策略、状态机怎么设计、考场上怎么自测边界用例。文章偏实战,建议你打开编辑器跟着写一遍,光看不练是记不住的。

1. 双机位C卷里,这道题为什么值得单独拿出来讲

1.1 考场环境先认清:ACM模式加全程录像

华为OD机考的双机位监考,意味着你的屏幕操作全程可见,一旦切出去查资料、看IDE自动提示、翻本地笔记,后台都有可能记录行为。所以备考阶段就要养成“盲打代码”的习惯,不能依赖编辑器补全,也不能指望考场上临时搜API文档。

更关键的是输入输出模式。LeetCode式刷题的接口通常是给你一个函数签名,比如public int[] filter(int[] samples, int minVal, int maxVal, int maxDiff, int minNormal),你只需要返回结果。但华为OD机考是ACM模式,题目只描述输入格式和输出格式,你得自己写Scanner、自己写readline、自己拼输出。很多第一次参加OD机考的人,题目逻辑其实想清楚了,结果卡在读取多行输入上,最后超时或者报运行时错误。

“采样过滤”恰好就是这种场景的典型代表。它不是一个复杂算法,不涉及图论、动态规划、贪心证明,它考的是你能不能把一个业务规则准确翻译成代码,并且在边界条件下不犯错。这类题在C卷中出现的频率相当高,难度适中,属于那种“会者不难、难者不会”的分水岭题型。

1.2 题名有迷惑性:它其实是一道状态机题

“采样过滤”这个名字听起来像数学题或者信号处理题,容易让人想复杂。实际上它就是一道非常典型的状态机模拟题,核心状态只有三个:当前是否已经存在参考值、连续正常计数值、参考值本身。

很多人在这一题上栽跟头,不是因为不会写循环,而是因为规则里有几个模糊地带没有想清楚。比如:第一个采样点算不算差值正常?一个值范围正常但与前一个参考值差值超标的点,要不要更新参考值?连续计数是从0开始还是从1开始?这些细节在不同版本的题目描述里有细微差异,直接决定了答案对不对。

这类题考的就是你把一段话翻译成几行if分支的能力。正因为如此,它特别适合用来检验一个人的编码基本功。你可以在备考阶段用它做专项训练,尤其是针对“读题后快速建模”和“多语言切换”这两个能力。

2. 过滤规则拆解:范围、差值、连续计数怎么协同

2.1 先把规则翻译成参数表

不同题库对这道题的文字描述略有差异,但核心规则基本一致。我在备考时整理了这样一套参数模型,你可以直接参考:

参数含义举例
n采样序列长度6
samples采样值序列[10, 13, 15, 8, 20, 30]
minVal采样值下限,低于它判为异常0
maxVal采样值上限,高于它判为异常20
maxDiff相邻正常采样之间允许的最大差值5
minNormal连续正常判定阈值,达到后才输出有效值2

过滤规则可以拆成四句话:

  1. 范围判断:如果sample[i]不在[minVal, maxVal]范围内,直接判为异常,输出0。
  2. 差值判断:将当前值与“参考值”的差绝对值与maxDiff比较,超过则判为异常。
  3. 连续计数:一个点只有同时通过范围判断和差值判断,才被计入“连续正常计数”;某个点异常时,计数清零。
  4. 输出判断:若当前连续正常计数达到minNormal,则该点输出原值,否则输出0。

第一句话很好理解,第二句话的关键在“参考值”这个词——它指的是最近一个有效采样值,这个值会在遍历过程中动态变化。第三句话则是整个题的状态核心。第四句话决定了最终每个位置输出的是原值还是0。

2.2 参考值更新策略,是最容易做错的地方

参考值更新是这道题最大的坑点。常见的做法有两种:

  • 策略A:只有完全正常的点(范围正常且差值正常)才更新参考值。
  • 策略B:只要范围正常的点,即使差值异常,也更新参考值。

这两种策略在平时的例子中可能输出相同,但一旦遇到连续跳变的数据,结果会完全不同。我构造一个序列来说明,比如:

samples = [50, 30, 31, 32] minVal = 0, maxVal = 100, maxDiff = 10, minNormal = 2

先按策略B推演:

  • i=0,v=50,范围正常,没有参考值,差值判断直接通过,validCount=1,1<2,输出0,ref=50。
  • i=1,v=30,范围正常,但|30-50|=20>10,差值异常,validCount清零,输出0,ref更新为30。
  • i=2,v=31,范围正常,|31-30|=1<=10,差值正常,validCount=1,1<2,输出0,ref=31。
  • i=3,v=32,范围正常,|32-31|=1<=10,差值正常,validCount=2,达到阈值,输出32。

最终结果是0 0 0 32

再按策略A推演:

  • i=0,同上,validCount=1,输出0,ref=50。
  • i=1,v=30,范围正常,但|30-50|=20>10,差值异常,validCount清零,输出0,ref保持50。
  • i=2,v=31,范围正常,但|31-50|=19>10,差值异常,validCount清零,输出0,ref保持50。
  • i=3,v=32,范围正常,但|32-50|=18>10,差值异常,validCount清零,输出0,ref保持50。

最终结果是0 0 0 0

看到区别了吗?同样是过滤,一个输出末尾的32,一个全部清零。哪个对?就取决于题目里对“有效参考值”的准确定义。我在刷题平台上见到的常见版本,多数采用策略B:只要值本身在合法范围内,就算差值异常也把它作为后续判断的新基准。这背后也有业务逻辑支撑——传感器采到一个跳变值,虽然和前面比差异大,但它本身是合法读数,后续数据更可能围绕它波动,而不是围绕旧值波动。

2.3 手算推演:完整走一遍状态流转

为了让你彻底看明白状态是怎么走的,我用一个具体例子把所有状态变化列出来。

输入:

6 10 13 15 8 20 30 0 20 5 2

逐步演算:

索引当前值范围正常差值正常连续计数是否输出参考值
010是(无参考值,直接通过)1否(1<2)10
113是(|13-10|=3<=5)2是(2>=2)13
215是(|15-13|=2<=5)315
38否(|8-15|=7>5)08
420否(|20-8|=12>5)020
530否(30>20)020(保持)

输出结果为:

0 13 15 0 0 0

这个表就是你的代码应该产生的完整状态流转。写完代码后,建议自己拿纸笔对着一张类似的表格手动走一遍,再和程序输出对比,能排查出很多选手思维上的误区。

3. 先用Python把状态机写明白,再往其他语言上平移

3.1 为什么先写Python版本

我的习惯是拿到这类题先用Python写一版“验证逻辑是否正确”。原因很简单:Python代码量最小,字符串处理、数组切片、输入解析都很直接,调试成本低。等逻辑验证通过了,再花时间翻译成Java或C++版本,针对语言特性做优化。

Python版本的一个重点是输入解析。ACM模式下,输入可能跨多行,也可能一行内有多个空格分隔。我用sys.stdin.read()一次性读全部内容再按空白切分,这样最稳妥,不会因为行读完而漏读。

import sys def main(): data = sys.stdin.read().strip().split() if not data: return idx = 0 n = int(data[idx]); idx += 1 samples = [] for _ in range(n): samples.append(int(data[idx])); idx += 1 min_val = int(data[idx]); idx += 1 max_val = int(data[idx]); idx += 1 max_diff = int(data[idx]); idx += 1 min_normal = int(data[idx]); idx += 1 res = [0] * n ref = 0 has_ref = False valid_count = 0 for i, v in enumerate(samples): range_ok = min_val <= v <= max_val diff_ok = (not has_ref) or (abs(v - ref) <= max_diff) if range_ok and diff_ok: valid_count += 1 ref = v has_ref = True if valid_count >= min_normal: res[i] = v elif range_ok: # 范围正常但差值异常:更新参考值,但清空连续正常计数 ref = v has_ref = True valid_count = 0 res[i] = 0 else: # 范围异常:不更新参考值,清空连续正常计数 valid_count = 0 res[i] = 0 print(" ".join(map(str, res))) if __name__ == "__main__": main()

这段代码的核心逻辑全部浓缩在for循环里。三个分支对应三种状态:

  1. 范围正常且差值正常:计数加一,更新参考值,达到阈值就输出原值。
  2. 范围正常但差值异常:更新参考值,计数清零,输出0。
  3. 范围异常:参考值不变,计数清零,输出0。

3.2 两个最容易搞错的细节

第一个细节是has_ref的初始值。第一个采样点没有“前一个参考值”可供比较,这时候差值判断应该直接通过,而不是去减一个初始化的0。如果初始化ref=0而不加has_ref判断,那么第一个点如果是负数或者正值,都会和0比一次,结果就可能错误。所以我在代码里用has_ref标记是否已经存在参考值,而不是用ref本身是否等于某个特殊值来判断。

第二个细节是范围正常但差值异常的分支到底更新不更新ref。我上面已经用[50,30,31,32]这个例子说明了两种策略的差异。如果你在做题时不确定该用哪种,有个笨办法:构造一个“连续跳变后逐步恢复稳定”的序列,把你的两种实现各跑一遍,看输出和题目样例是否一致。如果题目给的样例没有覆盖这个分支,那就去看题目文字描述里对“参考值”的原始定义。

另一个容易忽略的点是输出格式。ACM模式对输出格式要求很严,多一个空格、少一个换行都可能导致Presentation Error。Python里用" ".join(map(str, res))比手动循环print更好,天然不会在末尾多出空格。

3.3 从Python推导题目隐含假设

Python版本写完后,我会反推一遍题目隐含的三个假设,确保自己没有误解题意。

第一个假设是:范围异常的点是否影响参考值。我的答案是不影响,因为它的读数已经超出仪表量程,属于“无效读数”,不能作为后续参考。

第二个假设是:连续正常计数是在异常清0,还是在“范围正常但差值异常”时也清0。题目描述通常会说“异常点会使计数器归零”,而差值异常显然也是一种异常,所以我的解法中是清0的。

第三个假设是:输出结果长度必须和输入长度一致。也就是说,过滤后不会像传统滤波算法那样把数组变短,而是每个位置要么输出原值,要么输出0。这一点决定了你不能用append拼接有效值来缩短数组,必须初始化一个和n等长的结果数组。

想清楚这三个假设后再看题目,你会发现自己对规则的理解已经到位了。

4. Java、JS、C++、Go:同样逻辑,四种容易踩的坑

4.1 Java版本:Scanner不是不能用,大数据量别碰

Java最容易踩的坑就是用Scanner直接读大量输入。ScannernextInt()每次解析都会做很多类型检查和正则匹配,在数据量达到10^5级别时会明显变慢,严重时直接TLE。我在华为OD机考刷题时习惯用BufferedReaderStringTokenizer组合,速度和稳定性都更有保障。

import java.io.*; import java.util.*; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); int n = Integer.parseInt(st.nextToken()); int[] samples = new int[n]; st = new StringTokenizer(br.readLine()); for (int i = 0; i < n; i++) { samples[i] = Integer.parseInt(st.nextToken()); } st = new StringTokenizer(br.readLine()); int minVal = Integer.parseInt(st.nextToken()); int maxVal = Integer.parseInt(st.nextToken()); int maxDiff = Integer.parseInt(st.nextToken()); int minNormal = Integer.parseInt(st.nextToken()); int[] res = new int[n]; int ref = 0; boolean hasRef = false; int validCount = 0; for (int i = 0; i < n; i++) { int v = samples[i]; boolean rangeOk = v >= minVal && v <= maxVal; boolean diffOk = !hasRef || Math.abs(v - ref) <= maxDiff; if (rangeOk && diffOk) { validCount++; ref = v; hasRef = true; res[i] = validCount >= minNormal ? v : 0; } else if (rangeOk) { ref = v; hasRef = true; validCount = 0; res[i] = 0; } else { validCount = 0; res[i] = 0; } } StringBuilder sb = new StringBuilder(); for (int i = 0; i < n; i++) { if (i > 0) sb.append(' '); sb.append(res[i]); } System.out.println(sb); } }

Java版本里值得注意的一个细节是:Integer.parseInt遇到非数字字符串会抛NumberFormatException。如果题目输入的某个数超出int范围,或者格式不干净,这行会直接让程序崩溃。不过采样值通常不会超过int范围,所以这个问题更多是提醒你输入数据别被污染。

输出方面用StringBuilder拼接比反复System.out.print快很多。我见过一个同学每个结果都System.out.println一次,数据量一大直接超时。考场上一定要养成“一次性拼接完再输出”的习惯。

4.2 JavaScript(Node.js)版本:异步IO是最大的坑

JSer在机考上最容易翻车的不是算法,而是Node.js的异步IO模型。如果直接用readline模块,很容易出现主函数跑完了,数据还没读完的情况。我建议直接用fs.readFileSync('/dev/stdin', 'utf8')同步读取全部输入,简单粗暴且不会出错。

const fs = require('fs'); function main() { const input = fs.readFileSync('/dev/stdin', 'utf8').trim().split(/\s+/); let idx = 0; const n = parseInt(input[idx++]); const samples = []; for (let i = 0; i < n; i++) { samples.push(parseInt(input[idx++])); } const minVal = parseInt(input[idx++]); const maxVal = parseInt(input[idx++]); const maxDiff = parseInt(input[idx++]); const minNormal = parseInt(input[idx++]); const res = new Array(n).fill(0); let ref = 0; let hasRef = false; let validCount = 0; for (let i = 0; i < n; i++) { const v = samples[i]; const rangeOk = v >= minVal && v <= maxVal; const diffOk = !hasRef || Math.abs(v - ref) <= maxDiff; if (rangeOk && diffOk) { validCount++; ref = v; hasRef = true; res[i] = validCount >= minNormal ? v : 0; } else if (rangeOk) { ref = v; hasRef = true; validCount = 0; res[i] = 0; } else { validCount = 0; res[i] = 0; } } console.log(res.join(' ')); } main();

JS版本容易踩的坑有三个。

第一个是parseInt第二个参数。如果代码是parseInt("08"),在老版本Node里可能解析出0,因为前导0被当成八进制。稳妥做法是parseInt(input, 10),强制十进制,避免意外。

第二个是fs.readFileSync('/dev/stdin')在某些在线评测平台是否可用。华为OD机考使用的平台支持Node.js环境,/dev/stdin路径在Linux系统上是可以用的。如果你本地用Windows测试,路径会不同,建议在考场先跑一个最简单的“读一行、输出一行”的测试用例验证IO环境。

第三个是new Array(n).fill(0)的用法。fill(0)会正确把数组初始化为全0,这是ES6的方法,机考环境通常支持。不要用new Array(n).map(() => 0),因为new Array(n)创建的是稀疏数组,map会跳过空槽位,结果还是空数组。

4.3 C++版本:关掉同步流,避免cin超时

C++的坑相对最少,主要就是输入输出同步问题。如果用了cin却忘了关同步,大数据量下容易超时。核心代码逻辑和其他语言完全一致。

#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; vector<int> samples(n); for (int i = 0; i < n; ++i) { cin >> samples[i]; } int minVal, maxVal, maxDiff, minNormal; cin >> minVal >> maxVal >> maxDiff >> minNormal; vector<int> res(n, 0); int ref = 0; bool hasRef = false; int validCount = 0; for (int i = 0; i < n; ++i) { int v = samples[i]; bool rangeOk = v >= minVal && v <= maxVal; bool diffOk = !hasRef || abs(v - ref) <= maxDiff; if (rangeOk && diffOk) { ++validCount; ref = v; hasRef = true; res[i] = validCount >= minNormal ? v : 0; } else if (rangeOk) { ref = v; hasRef = true; validCount = 0; res[i] = 0; } else { validCount = 0; res[i] = 0; } } for (int i = 0; i < n; ++i) { if (i) cout << ' '; cout << res[i]; } cout << '\n'; return 0; }

这里我用了#include <bits/stdc++.h>这个万能头文件,在大部分在线评测系统上都能编译通过。如果考场用的编译器不允许这个头文件,就换成<iostream><vector><cstdlib><cmath>这几个具体头文件。

abs函数在C++中针对int类型重载,返回int绝对值,如果差值可能超过int范围,建议自己写一个long long abs(long long x)。这道题一般不会,但养成用long long处理中间计算的习惯没坏处。

还有vector<int> res(n, 0)这种初始化,比先声明再resize再循环赋值要简洁,也更能避免“忘记初始化导致垃圾值”的问题。

4.4 Go版本:bufio.Scanner的缓冲区上限是暗坑

Go在华为OD机考中这几年越来越常见。它编译快、运行快,就是API比较啰嗦。最容易踩的坑是bufio.Scanner默认最大token长度是64KB,如果某一行输入特别长,扫描器会直接报错退出。所以我会把缓冲调大,比如1MB。

package main import ( "bufio" "fmt" "os" "strconv" "strings" ) func main() { scanner := bufio.NewScanner(os.Stdin) scanner.Buffer(make([]byte, 1024*1024), 1024*1024) var nums []int for scanner.Scan() { fields := strings.Fields(scanner.Text()) for _, f := range fields { v, _ := strconv.Atoi(f) nums = append(nums, v) } } idx := 0 n := nums[idx] idx++ samples := make([]int, n) for i := 0; i < n; i++ { samples[i] = nums[idx] idx++ } minVal := nums[idx] idx++ maxVal := nums[idx] idx++ maxDiff := nums[idx] idx++ minNormal := nums[idx] idx++ res := make([]int, n) ref := 0 hasRef := false validCount := 0 for i, v := range samples { rangeOk := v >= minVal && v <= maxVal diffOk := !hasRef || abs(v-ref) <= maxDiff if rangeOk && diffOk { validCount++ ref = v hasRef = true if validCount >= minNormal { res[i] = v } } else if rangeOk { ref = v hasRef = true validCount = 0 } else { validCount = 0 } } out := make([]string, n) for i, v := range res { out[i] = strconv.Itoa(v) } fmt.Println(strings.Join(out, " ")) } func abs(x int) int { if x < 0 { return -x } return x }

Go里最需要注意的是strconv.Atoi会返回两个值,一个是数字,一个是错误。我在代码里用_忽略了错误,前提是保证输入格式完全合法。如果考场上想更严谨,可以加个判断,但通常不需要在这上面耗时间。

另外Go没有内置abs函数,很多人第一次写Go时会下意识地调用math.Abs,但math.Abs接收和返回的都是float64,用在这里会导致类型不匹配。要么自己写一个int版abs,要么用if v < ref比较之前先转换为float64再转回来。自己写最直接。

4.5 五种语言的对照:语法不同,状态机结构一模一样

把五种语言的代码放在一起看,你会发现在for循环内部的三个分支几乎是同一个套路:

  • 范围正常且差值正常:count加一,更新ref,够阈值输出原值。
  • 范围正常但差值异常:更新ref,count清零,输出0。
  • 范围异常:count清零,ref不动,输出0。

这其实就是核心算法思想与具体编码语言解耦的最好例证。刷题时,先把核心逻辑在脑子里或纸上画成状态转移图,代码只是把它翻译出来。翻译得久了,你会发现Java、C++、Go、Python、JS之间的切换越来越自然,剩下的差异主要是IO写法、类型声明、标准库API这些“语法皮囊”。

5. 机考实战:从读题到AC的完整策略

5.1 拿到题,先别急着打码,用十分钟做三件事

我在实际考场上如果遇到“采样过滤”这类题,不会立刻打开编辑器写循环,而是先把输入样例抄在草稿纸上,手动走一遍流程,确认自己对规则的理解没有偏差。具体来说就是做三件事:

第一,把输入参数列出来,确认每个变量名和取值范围。尤其要看清楚n到底代表序列长度还是别的含义,minNormal是从第几个点开始输出。

第二,构造一个能覆盖所有分支的用例。我会在草稿纸上设计几组极端数据,比如第一个点就超范围、所有点都正常但连续点数不够、一个点范围正常但差值异常。这些用例不是乱写的,它们是为了验证代码里的每个if分支都能被走到。

第三,想清楚输出格式。是用空格分隔还是换行分隔?结尾允不允许有多余空格?这题一般是空格分隔单行输出,所以我会用字符串拼接。

这三件事看起来只花几分钟,但能避免一大半“逻辑对了却过不了样例”的问题。很多人在考场上读完题就狂敲代码,最后死在题意理解偏差上,反而更浪费时间。

5.2 构造一套自己的边界测试用例

准备几个固定测试用例,每次写完代码都用它们做回归,是我刷题时比较受益的习惯。针对这道题,我常用这几个用例:

第一个用例,最小规模:

输入: 1 50 0 100 10 2 输出: 0

这个用例验证n=1minNormal=2时,即使范围差值都正常,也会因为计数不够而输出0。

第二个用例,阈值刚好为1:

输入: 2 50 51 0 100 10 1 输出: 50 51

验证minNormal=1时连续计数从第一个点达标,所有正常点都应该原值输出。

第三个用例,全超范围:

输入: 3 1000 1001 1002 0 100 10 2 输出: 0 0 0

验证范围异常时的清零逻辑和参考值不更新逻辑。

第四个用例,就是前面说的跳变恢复场景:

输入: 4 50 30 31 32 0 100 10 2 输出: 0 0 0 32

这个用例专门用来区分参考值更新策略。如果你的实现采用了策略A,跑这个用例会输出全0,说明和预期不一致,需要检查。

第五个用例,大数据量性能测试:

输入: 100000 0 1 2 3 4 5 ...(递增序列) 0 100000 1 1 输出: 0 1 2 3 4 5 ...(原样)

这个用例主要验证IO和循环不会超时。尤其是Java的Scanner版本和C++未关同步的cin版本,跑这个用例就可能露出性能问题。

5.3 考场上的时间分配和自测技巧

华为OD机考的题量和时间总体偏紧,我自己的节奏是:先快速扫一遍三题,优先做自己最有把握的那一题,建立信心和分数基础。遇到“采样过滤”这类模拟题时,正常应该在20到30分钟内完成从读题到AC的过程。如果超过40分钟还在一个细节上卡住,建议先跳过,留着回头再调试。

自测时不要只测题目给的样例。题目给的样例通常覆盖不全所有分支,你需要自己构造前面的四个用例逐个验证。每改一次代码,就把所有用例重跑一遍。如果有用例突然不一致,说明引入了新的回归问题。

另外,打印中间状态是一个很好的调试手段。在for循环里临时加上System.errconsole.error输出,把每次迭代的rangeOkdiffOkvalidCountref都打出来,和手算表对照,很快就能定位到是哪个分支出了问题。提交前把这些调试输出删掉,避免影响判题。

5.4 双机位监视下的备考方向

双机位监考环境下,你没法在考场上临时搜索“Java Scanner如何读一行数字”“Go怎么取绝对值”“JS readline怎么同步读”这类语法问题。所以备考阶段一定要把常用语言的IO模板背熟,形成肌肉记忆。

我自己的做法是准备一个“多语言模板速查表”,把每道高频题型的输入读取、输出拼接、核心循环结构都整理成标准范式。考前不看具体题目,只把速查表过一遍。这样到了考场上,即使心里紧张,手指也能条件反射式地敲出正确的IO代码。

双机位还有一个隐性影响:因为全程录像,很多人会在心理上有压迫感,感觉背后一直有人盯着,一紧张就把简单题写复杂了。针对这个,我建议在备考后半段每个礼拜都做一次“模拟考”:架一个摄像头对着自己,限时120分钟,期间不切屏、不查资料、不暂停,直接在牛客或平台题库上刷一套真题卷。等你习惯了被镜头盯着的环境,真正上考场时心理负担会小很多。

再补充一个关于“采样过滤”这类题型的延伸思考:它的核心思路是维护一个可变参考值和一个连续状态计数,这种模式在华为OD机考的其他题目中也很常见,比如“最长连续区间”“数组去重后保持相对顺序”“股票最大收益判断”等。如果你把这道题的状态机吃透了,后面遇到这类需要动态维护“上一次有效值”的题目,会非常有感觉。

我在实际带人刷题的过程中见过太多人栽在同一类细节上:不是不会写分支,而是没有先想清楚参考值到底怎么更新。希望这篇文章能帮你把这道题的逻辑彻底理顺,上考场前再也不用为它焦虑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询