
专栏导读
本专栏收录于《华为OD机试真题(Python/JS/C/C++)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。
一、题目描述
牛牛在超市采购商品,每件商品有对应价格(均为正整数)。给定预算 limit,要求选出连续一段商品,满足两条约束:
-
这段商品总价格严格大于 limit
-
这段商品中最贵商品价格 ≤ 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。
由于所有商品价格都是正整数,可以使用滑动窗口解决“连续区间和大于某个值时的最短长度”问题。定义左右指针 left、right 和当前窗口总和 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算法的适用场景,发现新题目,随时更新。

10万+

被折叠的 条评论
为什么被折叠?



