华为OD机试 - 小牛牛超市选品 - 滑动窗口(Python/JS/C/C++ 新系统 100分)

在这里插入图片描述

华为OD机试 新系统 统一考试题库清单(持续收录中)以及考点说明(Python/JS/C/C++)

专栏导读

本专栏收录于《华为OD机试真题(Python/JS/C/C++)》

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。

一、题目描述

牛牛在超市采购商品,每件商品有对应价格(均为正整数)。给定预算 limit,要求选出连续一段商品,满足两条约束:

  1. 这段商品总价格严格大于 limit

  2. 这段商品中最贵商品价格 ≤ limit

请找出满足以上两条规则的最短连续商品长度;不存在合法区间输出 0。

约束条件:

  • 0 < limit < 100

  • 0 < n ≤ 10

  • 0 < Aᵢ ≤ 10

二、输入描述

参数 1:商品数量 n
参数 2:预算上限 limit
参数 3:商品价格数组,数组元素代表每件商品价格 Aᵢ

三、输出描述

合法最短区间长度;无合法区间输出 0

四、测试用例

测试用例1:

1、输入

6 7
2 3 1 2 4 3

2、输出

3

3、说明

[2,4,3] 总和为 9,最大值 4≤7,长度为 3;不存在长度为 2 的合法区间

测试用例2:

1、输入

3 11
1 2 3

2、输出

0

3、说明

所有商品总和只有 6,无法严格大于 11

五、解题思路

题目要求寻找一个最短连续区间,同时满足两个条件:区间商品价格总和严格大于 limit,并且区间中的最大商品价格不能超过 limit

由于所有商品价格都是正整数,可以使用滑动窗口解决“连续区间和大于某个值时的最短长度”问题。定义左右指针 leftright 和当前窗口总和 sum。右指针从左向右依次扫描商品,只要当前商品价格不超过 limit,就加入窗口。若加入之后 sum > limit,说明当前窗口满足总价格条件,此时不断移动左指针并减去左侧商品价格,在仍然可能满足条件的过程中持续更新最短长度。由于数组元素全部为正数,删除左侧元素后窗口总和一定不会增加,因此这种收缩方式不会遗漏更短的合法答案。

第二个约束需要单独处理。如果某件商品价格 A[i] > limit,那么任何包含它的区间都会使区间最大价格超过 limit,因此合法区间绝不可能跨越这个位置。遇到这种商品时,可以把它看作一个天然的分隔点:将 sum 清零,并令 left = i + 1,从下一件商品重新建立滑动窗口。

需要特别注意,第一个条件要求总价格“严格大于” limit,所以代码中的判断必须是 sum > limit,不能使用 sum >= limit。例如 limit=5、区间 [2,3] 的总和虽然等于 5,但仍然不合法。

整个过程中每个元素最多被右指针加入窗口一次、被左指针移出窗口一次,因此时间复杂度为 O(n);除输入数组外,只使用几个变量,额外空间复杂度为 O(1)。相比枚举所有连续区间的 O(n²) 方法,滑动窗口更加高效,也更符合本题正整数数组的特点。

六、Python算法源码

import sys

data = list(map(int, sys.stdin.read().split()))

if not data:
    sys.exit(0)

n = data[0]
limit = data[1]
prices = data[2:2 + n]

left = 0
window_sum = 0
min_len = n + 1

for right in range(n):

    """
    当前商品价格超过 limit 时,
    任何包含它的区间都违反“最大价格 <= limit”。

    因此当前商品相当于一个分隔点:
    清空之前的窗口,从下一位置重新开始。
    """
    if prices[right] > limit:
        window_sum = 0
        left = right + 1
        continue

    window_sum += prices[right]

    """
    所有价格都是正整数。
    当前窗口总价严格大于 limit 后,
    不断移动左边界,可以寻找以 right 结尾的最短合法区间。
    """
    while window_sum > limit and left <= right:
        min_len = min(min_len, right - left + 1)

        window_sum -= prices[left]
        left += 1

print(0 if min_len == n + 1 else min_len)

七、JavaScript算法源码

const fs = require('fs');

const input = fs.readFileSync(0, 'utf8').trim();

if (input.length === 0) {
    process.exit(0);
}

const data = input.split(/\s+/).map(Number);

const n = data[0];
const limit = data[1];
const prices = data.slice(2, 2 + n);

let left = 0;
let sum = 0;
let minLen = n + 1;

for (let right = 0; right < n; right++) {

    /*
     * 如果当前商品价格大于 limit,
     * 那么所有包含它的区间都会违反“最大商品价格 <= limit”。
     *
     * 所以当前位置相当于一个分隔点,
     * 之前的滑动窗口全部作废,从下一位置重新开始。
     */
    if (prices[right] > limit) {
        sum = 0;
        left = right + 1;
        continue;
    }

    sum += prices[right];

    /*
     * 商品价格都是正整数。
     * 当窗口总和严格大于 limit 时,
     * 当前窗口已经合法。
     *
     * 不断删除左侧元素并缩小窗口,
     * 找到以 right 为右端点的最短合法区间。
     */
    while (sum > limit && left <= right) {
        minLen = Math.min(minLen, right - left + 1);

        sum -= prices[left];
        left++;
    }
}

console.log(minLen === n + 1 ? 0 : minLen);

八、C算法源码

#include <stdio.h>

int main(void) {
    int n, limit;

    if (scanf("%d %d", &n, &limit) != 2) {
        return 0;
    }

    /*
     * 根据题目条件 n <= 10,
     * 因此长度为 10 的数组即可保存全部商品价格。
     */
    int prices[10];

    for (int i = 0; i < n; i++) {
        scanf("%d", &prices[i]);
    }

    int left = 0;
    int sum = 0;
    int minLen = n + 1;

    for (int right = 0; right < n; right++) {

        /*
         * 当前商品单价超过 limit,
         * 则任何包含它的区间都不可能满足最大价格限制。
         *
         * 所以清空滑动窗口,从当前商品之后重新开始。
         */
        if (prices[right] > limit) {
            sum = 0;
            left = right + 1;
            continue;
        }

        sum += prices[right];

        /*
         * 数组中的商品价格全部为正整数。
         * 当 sum 严格大于 limit 时,
         * 不断收缩窗口左边界即可寻找更短的合法区间。
         */
        while (sum > limit && left <= right) {
            int len = right - left + 1;

            if (len < minLen) {
                minLen = len;
            }

            sum -= prices[left];
            left++;
        }
    }

    if (minLen == n + 1) {
        printf("0\n");
    } else {
        printf("%d\n", minLen);
    }

    return 0;
}

九、C++算法源码

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, limit;

    if (!(cin >> n >> limit)) {
        return 0;
    }

    vector<int> prices(n);

    for (int i = 0; i < n; i++) {
        cin >> prices[i];
    }

    int left = 0;
    int sum = 0;
    int minLen = n + 1;

    for (int right = 0; right < n; right++) {

        /*
         * 如果当前商品价格超过 limit,
         * 则所有包含该商品的连续区间都会违反最大价格条件。
         *
         * 因此把当前位置视为分隔点,
         * 清空窗口并从下一件商品重新计算。
         */
        if (prices[right] > limit) {
            sum = 0;
            left = right + 1;
            continue;
        }

        sum += prices[right];

        /*
         * 商品价格全部为正整数,
         * 因此窗口总和严格大于 limit 后,
         * 可以不断移动左边界来寻找更短的合法区间。
         */
        while (sum > limit && left <= right) {
            minLen = min(minLen, right - left + 1);

            sum -= prices[left];
            left++;
        }
    }

    cout << (minLen == n + 1 ? 0 : minLen) << '\n';

    return 0;
}


🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 新系统 200分)

🏆本文收录于,华为OD机试真题(Python/JS/C/C++)

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哪 吒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值