华为OD机试_2025 B卷_最差产品奖(Python,100分)(附详细解题思路)

题目描述

A公司准备对他下面的N个产品评选最差奖,
评选的方式是首先对每个产品进行评分,然后根据评分区间计算相邻几个产品中最差的产品。
评选的标准是依次找到从当前产品开始前M个产品中最差的产品,请给出最差产品的评分序列。

输入描述
第一行,数字M,表示评分区间的长度,取值范围是0<M<10000
第二行,产品的评分序列,比如[12,3,8,6,5],产品数量N范围是-10000 < N <10000

输出描述
评分区间内最差产品的评分序列

用例

输入3
12,3,8,6,5
输出3,3,5
说明

12,3,8 最差的是3

3,8,6 最差的是3

8,6,5 最差的是5

滑动窗口最小值问题详解

一、核心解题思路

问题分析
题目要求计算产品评分序列中每个长度为M的连续子序列(滑动窗口)的最小值。具体来说:

  • 给定一个长度为N的产品评分序列
  • 对于每个位置i(起始位置),计算从i开始连续M个产品中的最低评分
  • 最终输出所有窗口最小值组成的序列

关键特性

  1. 滑动窗口:窗口大小固定为M,每次向右移动一个位置
  2. 实时计算:需要高效获取每个新窗口的最小值
  3. 边界处理:当窗口超出序列边界时停止计算

暴力解法的问题
直接遍历每个窗口找最小值的时间复杂度是O(N×M),当N和M较大时(最大10000),计算量可达1亿次,效率低下。

优化方案:单调队列
使用双端队列维护一个单调递增序列:

  1. 队列中存储元素索引,对应值保持递增关系
  2. 队首元素始终是当前窗口的最小值
  3. 添加新元素时,从队尾移除比它大的元素
  4. 移动窗口时,检查队首元素是否过期

算法流程

初始化空队列
遍历序列中的每个评分:
    1. 移除过期元素(不在当前窗口的)
    2. 移除队尾比当前元素大的元素
    3. 将当前元素加入队尾
    4. 记录当前窗口最小值(队首元素)
二、完整代码实现
from collections import deque

def main():
    # 读取输入
    M = int(input().strip())
    scores = list(map(int, input().split(',')))
    
    # 边界检查
    if M <= 0 or M > len(scores):
        print("")
        return
    
    result = []  # 存储结果
    dq = deque()  # 双端队列(存储索引)
    
    for i in range(len(scores)):
        # 1. 移除过期元素(不在当前窗口内)
        if dq and dq[0] <= i - M:
            dq.popleft()
        
        # 2. 移除队尾比当前元素大的元素
        while dq and scores[dq[-1]] >= scores[i]:
            dq.pop()
        
        # 3. 添加当前元素索引
        dq.append(i)
        
        # 4. 记录窗口最小值(当窗口完全形成时)
        if i >= M - 1:
            result.append(str(scores[dq[0]]))
    
    # 输出结果
    print(",".join(result))

if __name__ == "__main__":
    main()

代码解析

  1. 输入处理

    • 读取窗口大小M
    • 读取评分序列并转为整数列表
    • 边界检查:M必须合法(0<M≤序列长度)
  2. 数据结构

    • 结果列表result存储最小值序列
    • 双端队列dq存储元素索引
  3. 核心算法

    • 移除过期元素:检查队首索引是否在窗口外(索引 ≤ i-M)
    • 维护单调性:从队尾移除比当前元素大的值
    • 添加新元素:将当前索引加入队尾
    • 记录结果:当窗口完全形成(i ≥ M-1)时记录队首值
  4. 输出处理

    • 将结果列表转为逗号分隔字符串
三、示例解析

输入:M=3, 序列=[12,3,8,6,5]

执行过程

当前索引当前值操作步骤队列状态窗口范围最小值
i=012添加索引0-
i=13移除比3大的12 → 添加索引1[0-1]-
i=28添加索引2[1,2][0-2]3
i=36移除比6大的8 → 添加索引3[1,3][1-3]3
i=45移除过期索引1 → 移除比5大的6 → 添加索引4[2-4]5

队列状态变化

i=0: 队列=[0] → 对应值[12]
i=1: 队列=[1] → 对应值[3](移除12)
i=2: 队列=[1,2] → 对应值[3,8]
i=3: 队列=[1,3] → 对应值[3,6](移除8)
i=4: 队列=[4] → 对应值[5](移除3和6)

输出:3,3,5

窗口最小值计算

  1. 窗口[12,3,8] → 最小值3
  2. 窗口[3,8,6] → 最小值3
  3. 窗口[8,6,5] → 最小值5
四、总结

关键要点

  • 单调队列维护:确保队列中元素对应值单调递增
  • 索引存储:通过索引同时获取元素值和位置信息
  • 过期检查:每次迭代检查队首是否在窗口内
  • 及时清理:添加新元素时移除无效的较大元素

适用场景

  • 需要高效计算滑动窗口最小值的场景
  • 实时数据流中的统计分析
  • 序列处理中的连续区间计算

此解法完全满足题目要求,通过维护单调队列高效解决了滑动窗口最小值问题,适合处理大规模数据序列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值