【华为OD】查找接口成功率最优时间段

【华为OD】查找接口成功率最优时间段

题目描述

服务之间交换的接口成功率作为服务调用关键质量特性,某个时间段内的接口失败率使用一个数组表示,数组中每个元素都是单位时间内失败率数值,数组中的数值为 0~100 的整数,给定一个数值(minAverageLost)表示某个时间段内平均失败率容忍值,即平均失败率小于等于 minAverageLost,找出数组中最长时间段,如果未找到则直接返回 NULL。

输入描述

输入有两行内容,第一行为 minAverageLost,第二行为数组,数组元素通过空格" "分隔,minAverageLost 及数组中元素取值范围为 0~100 的整数,数组元素的个数不会超过 100 个。

输出描述

找出平均值小于等于 minAverageLost 的最长时间段,输出数组下标对,格式{beginIndex}-{endIndex}(下标从 0 开始),如果同时存在多个最长时间段,则输出多个下标对且下标对之间使用空格" "拼接,多个下标对按下标从小到大排序。

示例

示例一

输入:

复制代码
1
0 1 2 3 4

输出:

复制代码
0-2

说明:

  • A、输入解释:minAverageLost=1,数组 [0, 1, 2, 3, 4]
  • B、前 3 个元素的平均值为 1,因此数组第一个至第三个数组下标,即 0-2

示例二

输入:

复制代码
2
0 0 100 2 2 99 0 2

输出:

复制代码
0-1 3-4 6-7

说明:

  • A、输入解释:minAverageLost = 2,数组 [0, 0, 100, 2, 2, 99, 0, 2]
  • B、通过计算小于等于 2 的最长时间段为:数组下标为 0-1 即 [0,0],数组下标为 3-4 即 [2, 2],数组下标为 6-7 即 [0, 2],这三个部分都满足平均值小于等于 2 的要求,因此输出 0-1 3-4 6-7

解题思路

这是一个寻找满足条件的最长连续子数组问题。需要找到所有平均值小于等于给定阈值的最长子数组。

核心思想:

  1. 枚举所有可能的连续子数组
  2. 计算每个子数组的平均值
  3. 找出满足条件的最长子数组
  4. 如果有多个相同长度的最长子数组,全部输出

我将提供两种解法:暴力枚举法滑动窗口优化法

解法一:暴力枚举法

枚举所有可能的连续子数组,计算平均值并找出满足条件的最长子数组。

Java实现

java 复制代码
import java.util.*;

public class Solution1 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int minAverageLost = sc.nextInt();
        sc.nextLine(); // 消费换行符
        
        String[] numStrs = sc.nextLine().split(" ");
        int[] nums = new int[numStrs.length];
        for (int i = 0; i < numStrs.length; i++) {
            nums[i] = Integer.parseInt(numStrs[i]);
        }
        
        int n = nums.length;
        int maxLen = 0;
        List<String> results = new ArrayList<>();
        
        // 枚举所有可能的子数组
        for (int i = 0; i < n; i++) {
            int sum = 0;
            for (int j = i; j < n; j++) {
                sum += nums[j];
                int len = j - i + 1;
                double avg = (double) sum / len;
                
                // 检查是否满足条件
                if (avg <= minAverageLost) {
                    if (len > maxLen) {
                        // 找到更长的子数组,清空之前的结果
                        maxLen = len;
                        results.clear();
                        results.add(i + "-" + j);
                    } else if (len == maxLen) {
                        // 找到相同长度的子数组,添加到结果中
                        results.add(i + "-" + j);
                    }
                }
            }
        }
        
        if (results.isEmpty()) {
            System.out.println("NULL");
        } else {
            System.out.println(String.join(" ", results));
        }
        
        sc.close();
    }
}

Python实现

python 复制代码
def solve_brute_force():
    min_average_lost = int(input())
    nums = list(map(int, input().split()))
    
    n = len(nums)
    max_len = 0
    results = []
    
    # 枚举所有可能的子数组
    for i in range(n):
        current_sum = 0
        for j in range(i, n):
            current_sum += nums[j]
            length = j - i + 1
            avg = current_sum / length
            
            # 检查是否满足条件
            if avg <= min_average_lost:
                if length > max_len:
                    # 找到更长的子数组,清空之前的结果
                    max_len = length
                    results = [f"{i}-{j}"]
                elif length == max_len:
                    # 找到相同长度的子数组,添加到结果中
                    results.append(f"{i}-{j}")
    
    if not results:
        print("NULL")
    else:
        print(" ".join(results))

solve_brute_force()

C++实现

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;

int main() {
    int minAverageLost;
    cin >> minAverageLost;
    cin.ignore(); // 忽略换行符
    
    string line;
    getline(cin, line);
    istringstream iss(line);
    vector<int> nums;
    int num;
    while (iss >> num) {
        nums.push_back(num);
    }
    
    int n = nums.size();
    int maxLen = 0;
    vector<string> results;
    
    // 枚举所有可能的子数组
    for (int i = 0; i < n; i++) {
        int sum = 0;
        for (int j = i; j < n; j++) {
            sum += nums[j];
            int len = j - i + 1;
            double avg = (double)sum / len;
            
            // 检查是否满足条件
            if (avg <= minAverageLost) {
                if (len > maxLen) {
                    // 找到更长的子数组,清空之前的结果
                    maxLen = len;
                    results.clear();
                    results.push_back(to_string(i) + "-" + to_string(j));
                } else if (len == maxLen) {
                    // 找到相同长度的子数组,添加到结果中
                    results.push_back(to_string(i) + "-" + to_string(j));
                }
            }
        }
    }
    
    if (results.empty()) {
        cout << "NULL" << endl;
    } else {
        for (int i = 0; i < results.size(); i++) {
            if (i > 0) cout << " ";
            cout << results[i];
        }
        cout << endl;
    }
    
    return 0;
}

解法二:滑动窗口优化法

使用滑动窗口的思想,对于每个起始位置,逐步扩展窗口直到不满足条件为止。

Java实现

java 复制代码
import java.util.*;

public class Solution2 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int minAverageLost = sc.nextInt();
        sc.nextLine(); // 消费换行符
        
        String[] numStrs = sc.nextLine().split(" ");
        int[] nums = new int[numStrs.length];
        for (int i = 0; i < numStrs.length; i++) {
            nums[i] = Integer.parseInt(numStrs[i]);
        }
        
        int n = nums.length;
        int maxLen = 0;
        List<String> results = new ArrayList<>();
        
        // 对每个起始位置使用滑动窗口
        for (int start = 0; start < n; start++) {
            int sum = 0;
            for (int end = start; end < n; end++) {
                sum += nums[end];
                int len = end - start + 1;
                double avg = (double) sum / len;
                
                if (avg <= minAverageLost) {
                    // 满足条件,检查是否需要更新结果
                    if (len > maxLen) {
                        maxLen = len;
                        results.clear();
                        results.add(start + "-" + end);
                    } else if (len == maxLen) {
                        results.add(start + "-" + end);
                    }
                } else {
                    // 不满足条件,但继续尝试更长的窗口
                    // 因为后面可能有更小的数字使平均值降低
                    continue;
                }
            }
        }
        
        if (results.isEmpty()) {
            System.out.println("NULL");
        } else {
            System.out.println(String.join(" ", results));
        }
        
        sc.close();
    }
}

Python实现

python 复制代码
def solve_sliding_window():
    min_average_lost = int(input())
    nums = list(map(int, input().split()))
    
    n = len(nums)
    max_len = 0
    results = []
    
    # 对每个起始位置使用滑动窗口
    for start in range(n):
        current_sum = 0
        for end in range(start, n):
            current_sum += nums[end]
            length = end - start + 1
            avg = current_sum / length
            
            if avg <= min_average_lost:
                # 满足条件,检查是否需要更新结果
                if length > max_len:
                    max_len = length
                    results = [f"{start}-{end}"]
                elif length == max_len:
                    results.append(f"{start}-{end}")
    
    if not results:
        print("NULL")
    else:
        print(" ".join(results))

solve_sliding_window()

C++实现

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;

int main() {
    int minAverageLost;
    cin >> minAverageLost;
    cin.ignore();
    
    string line;
    getline(cin, line);
    istringstream iss(line);
    vector<int> nums;
    int num;
    while (iss >> num) {
        nums.push_back(num);
    }
    
    int n = nums.size();
    int maxLen = 0;
    vector<string> results;
    
    // 对每个起始位置使用滑动窗口
    for (int start = 0; start < n; start++) {
        int sum = 0;
        for (int end = start; end < n; end++) {
            sum += nums[end];
            int len = end - start + 1;
            double avg = (double)sum / len;
            
            if (avg <= minAverageLost) {
                // 满足条件,检查是否需要更新结果
                if (len > maxLen) {
                    maxLen = len;
                    results.clear();
                    results.push_back(to_string(start) + "-" + to_string(end));
                } else if (len == maxLen) {
                    results.push_back(to_string(start) + "-" + to_string(end));
                }
            }
        }
    }
    
    if (results.empty()) {
        cout << "NULL" << endl;
    } else {
        for (int i = 0; i < results.size(); i++) {
            if (i > 0) cout << " ";
            cout << results[i];
        }
        cout << endl;
    }
    
    return 0;
}

解法三:优化的枚举法(推荐)

在暴力枚举的基础上进行一些优化,提高代码的可读性和效率。

Java实现

java 复制代码
import java.util.*;

public class Solution3 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int minAverageLost = sc.nextInt();
        sc.nextLine();
        
        String[] numStrs = sc.nextLine().split(" ");
        int[] nums = new int[numStrs.length];
        for (int i = 0; i < numStrs.length; i++) {
            nums[i] = Integer.parseInt(numStrs[i]);
        }
        
        List<String> result = findLongestValidSegments(nums, minAverageLost);
        
        if (result.isEmpty()) {
            System.out.println("NULL");
        } else {
            System.out.println(String.join(" ", result));
        }
        
        sc.close();
    }
    
    private static List<String> findLongestValidSegments(int[] nums, int threshold) {
        int n = nums.length;
        int maxLen = 0;
        List<String> results = new ArrayList<>();
        
        for (int i = 0; i < n; i++) {
            int sum = 0;
            for (int j = i; j < n; j++) {
                sum += nums[j];
                int len = j - i + 1;
                
                if (sum <= threshold * len) { // 避免浮点数计算
                    if (len > maxLen) {
                        maxLen = len;
                        results.clear();
                        results.add(i + "-" + j);
                    } else if (len == maxLen) {
                        results.add(i + "-" + j);
                    }
                }
            }
        }
        
        return results;
    }
}

Python实现

python 复制代码
def find_longest_valid_segments(nums, threshold):
    n = len(nums)
    max_len = 0
    results = []
    
    for i in range(n):
        current_sum = 0
        for j in range(i, n):
            current_sum += nums[j]
            length = j - i + 1
            
            if current_sum <= threshold * length:  # 避免浮点数计算
                if length > max_len:
                    max_len = length
                    results = [f"{i}-{j}"]
                elif length == max_len:
                    results.append(f"{i}-{j}")
    
    return results

def solve_optimized():
    min_average_lost = int(input())
    nums = list(map(int, input().split()))
    
    result = find_longest_valid_segments(nums, min_average_lost)
    
    if not result:
        print("NULL")
    else:
        print(" ".join(result))

solve_optimized()

C++实现

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;

vector<string> findLongestValidSegments(const vector<int>& nums, int threshold) {
    int n = nums.size();
    int maxLen = 0;
    vector<string> results;
    
    for (int i = 0; i < n; i++) {
        int sum = 0;
        for (int j = i; j < n; j++) {
            sum += nums[j];
            int len = j - i + 1;
            
            if (sum <= threshold * len) { // 避免浮点数计算
                if (len > maxLen) {
                    maxLen = len;
                    results.clear();
                    results.push_back(to_string(i) + "-" + to_string(j));
                } else if (len == maxLen) {
                    results.push_back(to_string(i) + "-" + to_string(j));
                }
            }
        }
    }
    
    return results;
}

int main() {
    int minAverageLost;
    cin >> minAverageLost;
    cin.ignore();
    
    string line;
    getline(cin, line);
    istringstream iss(line);
    vector<int> nums;
    int num;
    while (iss >> num) {
        nums.push_back(num);
    }
    
    vector<string> result = findLongestValidSegments(nums, minAverageLost);
    
    if (result.empty()) {
        cout << "NULL" << endl;
    } else {
        for (int i = 0; i < result.size(); i++) {
            if (i > 0) cout << " ";
            cout << result[i];
        }
        cout << endl;
    }
    
    return 0;
}

算法复杂度分析

解法一:暴力枚举法

  • 时间复杂度:O(N²),需要枚举所有可能的子数组
  • 空间复杂度:O(K),K为满足条件的最长子数组个数

解法二:滑动窗口优化法

  • 时间复杂度:O(N²),虽然使用了滑动窗口的思想,但仍需要枚举所有子数组
  • 空间复杂度:O(K),K为满足条件的最长子数组个数

解法三:优化的枚举法

  • 时间复杂度:O(N²),但避免了浮点数计算,提高了实际运行效率
  • 空间复杂度:O(K),K为满足条件的最长子数组个数

算法原理详解

核心思想

问题的本质是寻找满足平均值条件的最长连续子数组。对于子数组 [i, j],其平均值为:

复制代码
avg = sum(nums[i...j]) / (j - i + 1)

要使 avg <= threshold,等价于:

复制代码
sum(nums[i...j]) <= threshold * (j - i + 1)

这样可以避免浮点数计算,提高精度和效率。

优化技巧

  1. 避免浮点数计算 :将 sum/len <= threshold 转换为 sum <= threshold * len
  2. 及时更新结果:在遍历过程中及时更新最长长度和结果列表
  3. 结果去重:由于按顺序遍历,天然保证了结果的有序性

示例分析

示例一分析

数组:[0, 1, 2, 3, 4],threshold = 1

  • 子数组 [0]:平均值 = 0 ≤ 1 ✓,长度 = 1
  • 子数组 [0, 1]:平均值 = 0.5 ≤ 1 ✓,长度 = 2
  • 子数组 [0, 1, 2]:平均值 = 1 ≤ 1 ✓,长度 = 3
  • 子数组 [0, 1, 2, 3]:平均值 = 1.5 > 1 ✗

最长满足条件的子数组是 [0, 1, 2],输出 0-2

示例二分析

数组:[0, 0, 100, 2, 2, 99, 0, 2],threshold = 2

通过枚举所有子数组,找到满足条件的最长子数组长度为2,包括:

  • [0, 0] (下标0-1):平均值 = 0 ≤ 2 ✓
  • [2, 2] (下标3-4):平均值 = 2 ≤ 2 ✓
  • [0, 2] (下标6-7):平均值 = 1 ≤ 2 ✓

输出 0-1 3-4 6-7

总结

三种解法本质上都是暴力枚举,但在实现细节上有所不同:

  1. 暴力枚举法:最直观的实现,使用浮点数计算平均值
  2. 滑动窗口优化法:思路类似,但代码结构更清晰
  3. 优化的枚举法 :避免浮点数计算,提高精度和效率,推荐使用

对于这道题目,由于数组长度不超过100,所有方法都能高效运行。关键在于正确理解题意,找出所有满足条件的最长子数组,并按要求格式输出。

核心技巧:

  • 使用整数运算代替浮点数运算提高精度
  • 在遍历过程中及时更新最优解
  • 注意处理多个相同长度的最长子数组的情况
  • 输出格式要严格按照题目要求
相关推荐
塔中妖3 小时前
【华为OD】最大子矩阵和
算法·华为od·矩阵
努力学习的小廉3 小时前
深入了解linux系统—— 线程同步
linux·服务器·数据库·算法
数据爬坡ing3 小时前
从挑西瓜到树回归:用生活智慧理解机器学习算法
数据结构·深度学习·算法·决策树·机器学习
luoganttcc3 小时前
小鹏汽车 vla 算法最新进展和模型结构细节
人工智能·算法·汽车
云:鸢4 小时前
C语言链表设计及应用
c语言·开发语言·数据结构·链表
wallflower20204 小时前
滑动窗口算法在前端开发中的探索与应用
前端·算法
林木辛5 小时前
LeetCode热题 42.接雨水
算法·leetcode
MicroTech20255 小时前
微算法科技(NASDAQ: MLGO)采用量子相位估计(QPE)方法,增强量子神经网络训练
大数据·算法·量子计算
星梦清河5 小时前
宋红康 JVM 笔记 Day15|垃圾回收相关算法
jvm·笔记·算法