华为OD机试双机位C卷 - 采样过滤 (C++ & Python & JAVA & JS & GO)

采样过滤

2025华为OD机试双机位C卷 - 华为OD上机考试双机位C卷 200分题型

华为OD机试双机位C卷真题目录点击查看: 华为OD机试双机位C卷真题题库目录|机考题库 + 算法考点详解

题目描述

在做物理实验时,为了计算物体移动的速率,通过相机等工具周期性的采样物体移动距离。由于工具故障,采样数据存在误差甚至错误的情况。需要通过一个算法过滤掉不正确的采样值。

不同工具的故障模式存在差异,算法的各类门限会根据工具类型做相应的调整。

请实现一个算法,计算出给定一组采样值中正常值的最长连续周期。

判断第 i 个周期的采样数据 S[i] 是否正确的规则如下(假定物体移动速率不超过10个单元,前一个采样周期 S[i-1] ):

  • S[i] <= 0,即为错误值
  • S[i] < S[i-1],即为错误值
  • S[i] - S[i-1] >= 10,即为错误值
  • 其它情况为正常值

判断工具是否故障的规则如下:在M个周期内,采样数据为错误值的次数为T(次数可以不连续),则工具故障。

判断故障恢复的条件如下:产生故障后的P个周期内,采样数据一直为正常值,则故障恢复。

错误采样数据的处理方式:

  • 检测到故障后,丢弃从故障开始到故障恢复的采样数据。
  • 在检测到工具故障之前,错误的采样数据,则由最近一个正常值代替;如果前面没有正常的采样值,则丢弃此采样数据。

给定一段周期的采样数据列表S,计算正常值的最长连续周期。

输入描述

故障确认周期数和故障次数门限分别为M和T,故障恢复周期数为P。

第 i 个周期,检测点的状态为Si

输入为两行,格式如下:

复制代码
M T P
S1 S2 S3 ...

M、T和P的取值范围为[1, 100000]

Si取值范围为[0, 100000],i 从0开始编号

输出描述

一行输出正常值的最长连续周期

用例1

输入

none 复制代码
10 6 3
-1 1 2 3 100 10 13 9 10

输出

none 复制代码
8

说明

S[0],S[4],S[7],S[8]为错误值。S[0]之前没有正常的采样数据,丢弃S[0]。S[4]和S[7]不满足故障条件,此值分别由S[3]和S[6]代替,即S[4]为3,S[7]为13。替换后,S[8]小于S[7],也是错误值。

题解

思路:模拟

这道题就是细节比较多,实际难度不是很高。简单逻辑如下:

  1. 可以使用一个双端队列deque存储发生错误采样位置序号,用来处理判定故障发生的情况。额外注意,判断故障前应先移除队首小于等于 i -m的序号,因为题目要求的判断故障限制在m大小的窗口。

  2. 接下来定义hasLastValid表示这个采样之前是否有正常采样值,lastValidValue记录最近正常采样值。currentLen = 0表示当前正常连续周期数,maxCurrentLen = 0记录最大正常连续周期数。

  3. 接下来就是循环遍历输入检测序列,首先判断当前序列current是否属于发生错误(这部分参照下面代码):

    • 如果没有发生错误,执行currentLen += 1,hasLastValid = true,lastValidValue = current

    • 如果发生错误,将当前序号插入deque.然后分类处理

      • 如果当前dequeu.size() >= t,说明发生故障,进入故障恢复阶段。
      • 如果当前dequeu.size() < t,此时没有发生故障,分情况处理,以这个作为出发点在检测到工具故障之前,错误的采样数据,则由最近一个正常值代替;如果前面没有正常的采样值,则丢弃此采样数据。
        • hasLastValid == true情况下, 执行currentLen += 1, current = lastValid操作。
        • hasLastValid == false情况下, 不执行操作,因为不会影响currentLen
    1. 按照3的逻辑处理之后,记录下的maxLen就是结果。

c++

c++ 复制代码
#include<iostream>
#include<vector>
#include<string>
#include <utility> 
#include <sstream>
#include<algorithm> 
#include<cmath>
#include<map>
#include<deque>
using namespace std;


// 通用 切割函数 函数 将字符串str根据delimiter进行切割
vector<int> split(const string& str, const string& delimiter) {
    vector<int> result;
    size_t start = 0;
    size_t end = str.find(delimiter);
    while (end != string::npos) {
        result.push_back(stoi(str.substr(start, end - start)));
        start = end + delimiter.length();
        end = str.find(delimiter, start);
    }
    // 添加最后一个部分
    result.push_back(stoi(str.substr(start)));
    return result;
}


// 检验当前值是否合法
bool check(int cureent, bool hasLastValid, int lastValidValue) {
    if (cureent <= 0) return false;
    if (hasLastValid) {
        if (cureent < lastValidValue) return false;
        if (cureent - lastValidValue >= 10) return false;
    }
    return true;
}



int main() {
    // 窗口大小 窗口出现次数  恢复
    int m, t, p;
    cin >> m >> t >> p;
    // 忽略换行符
    cin.ignore();
    string input;
    getline(cin, input);
    vector<int> seq = split(input, " ");

    int n  = seq.size();
    
    // 最近一个有效正常值
    bool hasLastValid = false;
    int lastValidValue = 0;
         
    
    // 当前正常连续周期数
    int currentLen = 0;
    // 最大正常连续周期数
    int maxLen = 0;
    
    // 最近 M 个周期内的错误位置
    deque<int> errorWindow;
    
    for (int i = 0; i < n; ) {
        int current = seq[i];
        bool isNormal = check(current, hasLastValid, lastValidValue);

        // 移除超过m窗口的错误
        while (!errorWindow.empty() && errorWindow.front() <= i - m) {
            errorWindow.pop_front();
        }

        // 正常值
        if (isNormal) {
            hasLastValid = true;
            lastValidValue = current;
            currentLen++;
            maxLen = max(currentLen, maxLen);
        // 异常值
        } else {
            // 加入队列中
            errorWindow.push_back(i);
            // 没有发生故障
            if (errorWindow.size() < t) {
                // 并且前面有效值, 没有的话,应该直接丢弃,不会加长度不处理
                if (hasLastValid) {
                    seq[i] = lastValidValue;
                    currentLen++;
                    maxLen = max(currentLen, maxLen); 
                }
            // 从这里发生故障
            } else {
                // 按照这里逻辑直接进行故障恢复
                int j = i + 1;
                bool recoveryHasLastValid = false;
                int recoveryLastValidValue = 0;
                // 连续正常区间个数
                int pcount = 0;
                while (j < n && pcount < p) {
                    int recoveryCurrent = seq[j];
                    bool recoveryIsNormal = check(recoveryCurrent, recoveryHasLastValid, recoveryLastValidValue);
                    if (recoveryIsNormal) {
                        pcount++;
                        recoveryHasLastValid = true;
                        recoveryLastValidValue = recoveryCurrent;

                    // 连续被终止
                    } else {
                        pcount = 0;
                        recoveryHasLastValid = false;
                        recoveryLastValidValue = 0;
                    }
                    j++;
                }
                // 全部丢弃
                currentLen = 0;
                errorWindow.clear();
                // 认定之前没有合法值
                hasLastValid = false;
                lastValidValue = 0;
                i = j - 1; 
            }
        }
        i++;
    }


    cout << maxLen << endl;

    return 0;
}

JAVA

JAVA 复制代码
import java.util.*;
public class Main {

    // 检验当前值是否合法
    static boolean check(int current, boolean hasLastValid, int lastValidValue) {
        if (current <= 0) return false;
        if (hasLastValid) {
            if (current < lastValidValue) return false;
            if (current - lastValidValue >= 10) return false;
        }
        return true;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        // 窗口大小 窗口出现次数  恢复
        int m = sc.nextInt();
        int t = sc.nextInt();
        int p = sc.nextInt();
        sc.nextLine(); // 忽略换行

        // 读取采样序列
        String line = sc.nextLine();
        String[] parts = line.split(" ");
        int n = parts.length;
        int[] seq = new int[n];
        for (int i = 0; i < n; i++) seq[i] = Integer.parseInt(parts[i]);

        // 最近一个有效正常值
        boolean hasLastValid = false;
        int lastValidValue = 0;

        // 当前正常连续周期数
        int currentLen = 0;
        // 最大正常连续周期数
        int maxLen = 0;

        // 最近 M 个周期内的错误位置
        Deque<Integer> errorWindow = new LinkedList<>();

        for (int i = 0; i < n; ) {
            int current = seq[i];
            boolean isNormal = check(current, hasLastValid, lastValidValue);

            // 移除超过m窗口的错误
            while (!errorWindow.isEmpty() && errorWindow.peekFirst() <= i - m) {
                errorWindow.pollFirst();
            }

            // 正常值
            if (isNormal) {
                hasLastValid = true;
                lastValidValue = current;
                currentLen++;
                maxLen = Math.max(currentLen, maxLen);
            // 异常值
            } else {
                // 加入队列中
                errorWindow.addLast(i);
                // 没有发生故障
                if (errorWindow.size() < t) {
                    // 并且前面有效值, 没有的话,应该直接丢弃,不会加长度不处理
                    if (hasLastValid) {
                        seq[i] = lastValidValue;
                        currentLen++;
                        maxLen = Math.max(currentLen, maxLen);
                    }
                // 从这里发生故障
                } else {
                    // 按照这里逻辑直接进行故障恢复
                    int j = i + 1;
                    boolean recoveryHasLastValid = false;
                    int recoveryLastValidValue = 0;
                    // 连续正常区间个数
                    int pcount = 0;
                    while (j < n && pcount < p) {
                        int recoveryCurrent = seq[j];
                        boolean recoveryIsNormal = check(recoveryCurrent, recoveryHasLastValid, recoveryLastValidValue);
                        if (recoveryIsNormal) {
                            pcount++;
                            recoveryHasLastValid = true;
                            recoveryLastValidValue = recoveryCurrent;
                        // 连续被终止
                        } else {
                            pcount = 0;
                            recoveryHasLastValid = false;
                            recoveryLastValidValue = 0;
                        }
                        j++;
                    }
                    // 全部丢弃
                    currentLen = 0;
                    errorWindow.clear();
                    // 认定之前没有合法值
                    hasLastValid = false;
                    lastValidValue = 0;
                    i = j - 1; 
                }
            }
            i++;
        }

        System.out.println(maxLen);
    }
}

Python

python 复制代码
import sys
from collections import deque

# 检验当前值是否合法
def check(current, has_last_valid, last_valid_value):
    # 当前值 <= 0 为错误值
    if current <= 0:
        return False
    if has_last_valid:
        # 如果有上一个正常值,当前值小于上一个也为错误
        if current < last_valid_value:
            return False
        # 当前值超过上一个正常值 >=10,也为错误
        if current - last_valid_value >= 10:
            return False
    return True

def main():
    # 窗口大小 m,窗口出现次数 t,恢复周期 p
    m, t, p = map(int, sys.stdin.readline().split())
    # 读取采样序列
    seq = list(map(int, sys.stdin.readline().split()))
    n = len(seq)

    # 最近一个有效正常值
    has_last_valid = False
    last_valid_value = 0

    # 当前正常连续周期数
    current_len = 0
    # 最大正常连续周期数
    max_len = 0

    # 最近 M 个周期内的错误位置
    error_window = deque()

    i = 0
    while i < n:
        current = seq[i]
        is_normal = check(current, has_last_valid, last_valid_value)

        # 移除超过 m 窗口的错误
        while error_window and error_window[0] <= i - m:
            error_window.popleft()

        # 正常值
        if is_normal:
            has_last_valid = True
            last_valid_value = current
            current_len += 1
            max_len = max(max_len, current_len)
        else:
            # 异常值加入队列
            error_window.append(i)
            # 没有发生故障
            if len(error_window) < t:
                # 前面有正常值,用最近正常值替换
                if has_last_valid:
                    seq[i] = last_valid_value
                    current_len += 1
                    max_len = max(max_len, current_len)
                # 前面没有正常值,直接丢弃,不增加长度
            else:
                # 从这里发生故障,寻找恢复
                j = i + 1
                recovery_has_last_valid = False
                recovery_last_valid_value = 0
                pcount = 0
                while j < n and pcount < p:
                    recovery_current = seq[j]
                    recovery_is_normal = check(recovery_current, recovery_has_last_valid, recovery_last_valid_value)
                    if recovery_is_normal:
                        pcount += 1
                        recovery_has_last_valid = True
                        recovery_last_valid_value = recovery_current
                    else:
                        pcount = 0
                        recovery_has_last_valid = False
                        recovery_last_valid_value = 0
                    j += 1
                # 故障期间丢弃
                current_len = 0
                error_window.clear()
                has_last_valid = False
                last_valid_value = 0
                i = j - 1  # 跳过故障恢复阶段
        i += 1

    print(max_len)

if __name__ == "__main__":
    main()

JavaScript

js 复制代码
// Node 链表节点
class Node {
    constructor(val) {
        this.val = val;
        this.next = null;
        this.prev = null;
    }
}

// 双端链表 deque
class Deque {
    constructor() {
        this.head = null;
        this.tail = null;
        this.size = 0;
    }
    pushBack(val) {
        let node = new Node(val);
        if (!this.tail) {
            this.head = this.tail = node;
        } else {
            this.tail.next = node;
            node.prev = this.tail;
            this.tail = node;
        }
        this.size++;
    }
    popFront() {
        if (!this.head) return null;
        let val = this.head.val;
        this.head = this.head.next;
        if (this.head) this.head.prev = null;
        else this.tail = null;
        this.size--;
        return val;
    }
    front() {
        return this.head ? this.head.val : null;
    }
    getSize() {
        return this.size;
    }
}

// ACM 模式
const readline = require('readline');
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

let inputLines = [];
rl.on('line', (line) => {
    inputLines.push(line);
}).on('close', () => {
    // 解析输入
    const [m, t, p] = inputLines[0].split(' ').map(Number);
    const seq = inputLines[1].split(' ').map(Number);
    const n = seq.length;

    // 检验当前值是否合法
    function check(current, hasLastValid, lastValidValue) {
        if (current <= 0) return false;
        if (hasLastValid) {
            if (current < lastValidValue) return false;
            if (current - lastValidValue >= 10) return false;
        }
        return true;
    }

    // 最近一个有效正常值
    let hasLastValid = false;
    let lastValidValue = 0;

    // 当前正常连续周期数
    let currentLen = 0;
    // 最大正常连续周期数
    let maxLen = 0;

    // 最近 M 个周期内的错误位置,用自定义 deque 保证前端删除 O(1), 实际机考可能并没这么大数据量,可以先用数组模拟试试
    let errorWindow = new Deque();

    let i = 0;
    while (i < n) {
        let current = seq[i];
        let isNormal = check(current, hasLastValid, lastValidValue);

        // 移除超过 m 窗口的错误
        while (errorWindow.getSize() > 0 && errorWindow.front() <= i - m) {
            errorWindow.popFront();
        }

        // 正常值
        if (isNormal) {
            hasLastValid = true;
            lastValidValue = current;
            currentLen++;
            maxLen = Math.max(maxLen, currentLen);
        } else {
            // 异常值加入队列中
            errorWindow.pushBack(i);
            // 没有发生故障
            if (errorWindow.getSize() < t) {
                if (hasLastValid) {
                    seq[i] = lastValidValue;
                    currentLen++;
                    maxLen = Math.max(maxLen, currentLen);
                }
            } else {
                // 从这里发生故障,寻找恢复
                let j = i + 1;
                let recoveryHasLastValid = false;
                let recoveryLastValidValue = 0;
                let pcount = 0;
                while (j < n && pcount < p) {
                    let recoveryCurrent = seq[j];
                    let recoveryIsNormal = check(recoveryCurrent, recoveryHasLastValid, recoveryLastValidValue);
                    if (recoveryIsNormal) {
                        pcount++;
                        recoveryHasLastValid = true;
                        recoveryLastValidValue = recoveryCurrent;
                    } else {
                        pcount = 0;
                        recoveryHasLastValid = false;
                        recoveryLastValidValue = 0;
                    }
                    j++;
                }
                // 故障期间丢弃
                currentLen = 0;
                errorWindow = new Deque();
                hasLastValid = false;
                lastValidValue = 0;
                i = j - 1;
            }
        }
        i++;
    }

    console.log(maxLen);
});

Go

go 复制代码
package main

import (
    "bufio"
    "container/list"
    "fmt"
    "os"
    "strconv"
    "strings"
)

// 检验当前值是否合法
func check(current int, hasLastValid bool, lastValidValue int) bool {
    if current <= 0 {
        return false
    }
    if hasLastValid {
        if current < lastValidValue {
            return false
        }
        if current-lastValidValue >= 10 {
            return false
        }
    }
    return true
}

func main() {
    reader := bufio.NewReader(os.Stdin)
    // 窗口大小 窗口出现次数 恢复
    line1, _ := reader.ReadString('\n')
    parts := strings.Fields(line1)
    m, _ := strconv.Atoi(parts[0])
    t, _ := strconv.Atoi(parts[1])
    p, _ := strconv.Atoi(parts[2])

    line2, _ := reader.ReadString('\n')
    seqParts := strings.Fields(line2)
    n := len(seqParts)
    seq := make([]int, n)
    for i := 0; i < n; i++ {
        seq[i], _ = strconv.Atoi(seqParts[i])
    }

    // 最近一个有效正常值
    hasLastValid := false
    lastValidValue := 0

    currentLen := 0
    maxLen := 0

    // 最近 M 个周期内的错误位置,用 container/list 高效 O(1) 删除前端
    errorWindow := list.New()

    i := 0
    for i < n {
        current := seq[i]
        isNormal := check(current, hasLastValid, lastValidValue)

        // 移除超过 m 窗口的错误
        for errorWindow.Len() > 0 {
            front := errorWindow.Front()
            if front.Value.(int) <= i-m {
                errorWindow.Remove(front)
            } else {
                break
            }
        }

        // 正常值
        if isNormal {
            hasLastValid = true
            lastValidValue = current
            currentLen++
            if currentLen > maxLen {
                maxLen = currentLen
            }
        } else {
            // 异常值加入队列中
            errorWindow.PushBack(i)
            if errorWindow.Len() < t {
                if hasLastValid {
                    seq[i] = lastValidValue
                    currentLen++
                    if currentLen > maxLen {
                        maxLen = currentLen
                    }
                }
            } else {
                // 故障处理,寻找恢复
                j := i + 1
                recoveryHasLastValid := false
                recoveryLastValidValue := 0
                pcount := 0
                for j < n && pcount < p {
                    recoveryCurrent := seq[j]
                    recoveryIsNormal := check(recoveryCurrent, recoveryHasLastValid, recoveryLastValidValue)
                    if recoveryIsNormal {
                        pcount++
                        recoveryHasLastValid = true
                        recoveryLastValidValue = recoveryCurrent
                    } else {
                        pcount = 0
                        recoveryHasLastValid = false
                        recoveryLastValidValue = 0
                    }
                    j++
                }
                // 故障期间丢弃
                currentLen = 0
                errorWindow.Init()
                hasLastValid = false
                lastValidValue = 0
                i = j - 1
            }
        }
        i++
    }

    fmt.Println(maxLen)
}
相关推荐
Tony_yitao5 天前
15.华为OD机考 - 执行任务赚积分
数据结构·算法·华为od·algorithm
无限码力6 天前
华为OD机试真题 - 最长广播响应 (C++ & Python & JAVA & JS & GO)
华为od·华为od机试·od机考·华为od上机考试真题·华为od机试-最长广播响应·华为od-最长广播响应
我是华为OD~HR~栗栗呀7 天前
(华为od)Python面经-20届
华为od
兩尛12 天前
猴子爬山od
算法·华为od
我是华为OD~HR~栗栗呀13 天前
23届(华为od)-C开发面经
java·c语言·c++·python·华为od·华为·面试
兩尛13 天前
【华为OD机试】运维日志排序
java·数据结构·华为od
Tony_yitao16 天前
12.华为OD机试 - N个选手比赛前三名、比赛(Java 双机位A卷 100分)
java·算法·华为od·algorithm
我是华为OD~HR~栗栗呀17 天前
华为OD-C面经-23届学院哦
java·c++·python·华为od·华为·面试
无限码力20 天前
【全网首发】2025华为OD机试双机位C卷(机考全真题库)含考点说明(华为OD上机考试双机位C卷)
华为od·华为od机考·华为od机试·华为od机试双机位c卷·华为od机考双机位c卷·华为od上机考试双机位c卷