查找接口成功率最优时间段 (c卷)

分值: 100分

题目描述

服务之间交换的接口成功率作为服务调用关键质量特性,某个时间段内的接口失败率使用一个数组表示。

数组中每个元素都是单位时间内失败率数值,数组中的数值为0~100的整数,

给定一个数值(minAverageLost)表示某个时间段内平均失败率容忍值,即平均失败率小于等于minAverageLost.找出数组中最长时间段,

如果未找到则直接返回NULL。

输入描述

有两行内容,

第一行为 minAverageLost,

第二行为数组,数组元素通过空格(" ")分隔,

minAverageLost及数组中元素取值范围为0~100的整数,数组元素的个数不会超过100个

输出描述

找出平均值小于等于minAverageLost的最长时间段,输出数组下标对,格式{beginIndex}-{endIndex} (下标从0开始),

如果同时存在多个最长时间段,则输出多个下标对且下标对之间使用空格(" ")拼接,多个下标对按下标从小到大排序。

示例1

输入:

1

0 1 2 3 4

输出:

0-2

说明:

A、输入解释:minAverageLost=1,数组[0, 1, 2, 3, 4]

B、前3个元素的平均值为1,因此数组第一个至第三个数组下标,即0-2
示例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

代码思路

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

public class Main {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 读取目标平均值
        int eve = in.nextInt();
        // 读取并丢弃换行符(因为nextInt后光标还在当前行)
        String x = in.nextLine();
        // 读取整行数字并分割
        String[] st = in.nextLine().split(" ");
        // 将字符串数组转换为整数数组
        int[] nums = new int[st.length];
        for (int i = 0; i < st.length; i++) {
            nums[i] = Integer.parseInt(st[i]);
        }
        // 调用核心方法找出满足条件的子数组
        minAverageList(nums, eve);
    }

    public static void minAverageList(int[] nums, int eve) {
        // dp[i]存储从0到i的前缀和(元素之和)
        int[] dp = new int[nums.length];
        // f[i]存储以i结尾的满足条件的最大子数组长度
        int[] f = new int[nums.length];
        // 哈希表用于记录前缀和的索引,但此处代码未实际使用,后续需要完善
        Map<Integer, Integer> map = new HashMap<>();

        // 初始化第一个元素的前缀和
        dp[0] = nums[0];
        // 如果第一个元素本身就小于目标平均值,则满足条件的长度为1
        if (dp[0] < eve) {
            f[0] = 1;
        }
        // 记录全局最大子数组长度
        int maxl = f[0];

        // 遍历数组,计算每个位置的最长满足条件子数组
        for (int i = 1; i < nums.length; i++) {
            // 计算前缀和
            dp[i] = dp[i - 1] + nums[i];
            // 情况1:从0到i的整个前缀都满足条件
            if (dp[i] <= (i + 1) * eve) {
                f[i] = i + 1;
            } else {
                // 情况2:向前查找满足条件的子数组
                // 遍历j从i-1到0,k表示子数组长度(从1开始)
                for (int j = i - 1, k = 1; j >= 0; j--, k++) {
                    // 如果从j+1到i的子数组和满足条件
                    // 即 dp[i]-dp[j] <= k*eve
                    if (dp[i] - dp[j] <= k * eve) {
                        f[i] = k;
                    }
                }
            }
            // 更新全局最大值
            maxl = Math.max(maxl, f[i]);
        }

        // 构建结果字符串,输出所有满足最大长度的子数组区间
        StringBuffer st = new StringBuffer();
        for (int i = 0; i < nums.length; i++) {
            // 如果以i结尾的子数组长度等于最大值
            if (f[i] == maxl) {
                // 计算起始和结束索引(索引从0开始)
                // 起始 = i - 长度 + 1, 结束 = i
                st.append(i - maxl + 1 + "-" + i + " ");
            }
        }
        System.out.println(st);
    }
}

解题思路:

  1. 输入最小平均失败率和失败率数据。
  2. 使用前缀和数组 psum 记录从头开始到每个位置的失败率累加和。
  3. 使用两层循环遍历所有可能的时间段,通过前缀和数组判断平均失败率是否小于等于给定的阈值。
  4. 如果找到一个满足条件的时间段,比较其长度是否大于当前已知的最大长度,更新最大长度和结果数组。
  5. 最终输出所有满足条件的最长时间段。
java 复制代码
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * @author code5bug
 */
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 读取目标平均损失值
        int minAverageLost = scanner.nextInt();

        // 读取所有损失值并存储到列表中
        List<Integer> losts = new ArrayList<>();
        while (scanner.hasNextInt()) {
            losts.add(scanner.nextInt());
        }

        int n = losts.size();  // 获取损失值数量

        // 计算前缀和数组,psum[i]表示前i个元素的和(psum[0]=0)
        int[] psum = new int[n + 1];
        for (int i = 0; i < n; i++) {
            psum[i + 1] = psum[i] + losts.get(i);
        }

        int maxLength = 1;  // 记录满足条件的最大子数组长度,初始值为1
        List<int[]> result = new ArrayList<>();  // 存储所有满足最大长度的子数组区间

        // 双重循环遍历所有可能的子数组
        for (int l = 0; l < n; l++) {  // l表示子数组起始位置
            // 优化:从l+maxLength-1开始遍历,跳过长度小于当前最大值的子数组
            for (int r = l + maxLength - 1; r < n; r++) {
                int length = r - l + 1;  // 计算当前子数组长度
                
                // 检查子数组l到r的平均损失是否小于等于目标值
                // 等价于:子数组和 <= 目标平均值 × 长度
                if (psum[r + 1] - psum[l] <= minAverageLost * length) {
                    // 找到更长的时间段
                    if (length > maxLength) {
                        maxLength = length;  // 更新最大长度
                        result.clear();  // 清空之前的结果
                        result.add(new int[]{l, r});  // 添加新区间
                    } else if (length == maxLength) {  // 长度与当前最大值相等
                        result.add(new int[]{l, r});  // 添加新的区间
                    }
                }
            }
        }

        // 打印输出结果
        for (int[] p : result) {
            System.out.print(p[0] + "-" + p[1] + " ");
        }
    }
}
相关推荐
乐迪信息21 小时前
乐迪信息:目标检测算法+AI摄像机:煤矿全场景识别方案
人工智能·物联网·算法·目标检测·目标跟踪·语音识别
Tony Bai1 天前
高并发后端:坚守 Go,还是拥抱 Rust?
开发语言·后端·golang·rust
wjs20241 天前
Swift 类型转换
开发语言
前端小L1 天前
贪心算法专题(十):维度权衡的艺术——「根据身高重建队列」
javascript·算法·贪心算法
方得一笔1 天前
自定义常用的字符串函数(strlen,strcpy,strcmp,strcat)
算法
秃了也弱了。1 天前
python实现定时任务:schedule库、APScheduler库
开发语言·python
weixin_440730501 天前
java数组整理笔记
java·开发语言·笔记
Dillon Dong1 天前
从C到Simulink: 使用STM32硬件支持包后为什么还不支持PC仿真ARM建模程序
c语言·stm32·simulink
Xの哲學1 天前
Linux SMP 实现机制深度剖析
linux·服务器·网络·算法·边缘计算
Thera7771 天前
状态机(State Machine)详解:原理、优缺点与 C++ 实战示例
开发语言·c++