题目描述

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

输入描述

  • 第一行,数字最差产品奖_最小值_03,表示评分区间的长度,取值范围是最差产品奖_System_05
  • 第二行,产品的评分序列,比如[12, 3, 8, 6, 5],产品数量N的范围是最差产品奖_最小值_06

输出描述

  • 评分区间内最差产品的评分序列

用例

--输入
3
12,3,8,6,5

--输出
3,3,5

--说明
12,3,8  最差的是3
3,8,6   最差的是3
8,6,5   最差的是5

题目解析

数据量范围:10^4   双重循环可能失败
本质上也就是一个滑动窗口求区间内的最小值
  • 先思考暴力解法
  • 从数组的开始,计算区间 M 的最小值
  • 然后不断向后移动,不断计算最小值
  • 这样的话,对于每一个区间 M 都要循环一次找到最小值
  • 暴力解决办法好像也可以
  • 有没有更优的解法呢?

参考

https://fcqian.blog.csdn.net/article/details/128385627

单调队列解法

  • 维护一个单调递增的队列,保证队列中的第一个元素是最小值。队列长度最大等于窗口长度
  • 当窗口向右移动,那么判断队列头部的元素是不是滑出窗口外了
  • 如果滑出了,那么移除队列头部的元素
  • 如果没有则进行当前元素的判断
  • 判断队列尾部的元素和当前元素的大小
  • 如果当前元素大于队列尾部的元素,则直接把当前元素加入队列
  • 否则,移除队列尾部元素之后,直到当前元素大于队列尾部的元素,再把当前元素加入队列

show code

暴力+单调队列解法

package com.hw;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;

/**
 * desc :    <a href="https://fcqian.blog.csdn.net/article/details/128385627">最差产品奖</a>
 * <p>
 * create time : 2023/7/23 14:14
 */
public class BadProductRank {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        while (in.hasNextLine()) {
            // 评分区间长度 M
            int M = Integer.parseInt(in.nextLine());

            // 产品的评分序列
            String line = in.nextLine();
            String[] split = line.split(",");
            int[] score = new int[split.length];
            for (int i = 0; i < split.length; i++) {
                score[i] = Integer.parseInt(split[i]);
            }

            //badProductRank(M, score);
            betterWay(M, score);
        }
    }

    // 数据量范围:10^4   双重循环可能失败
    // 本质上也就是一个滑动窗口求区间内的最小值

    // 暴力解法
    private static void badProductRank(int M, int[] score) {
        List<Integer> ans = new ArrayList<>();
        for (int i = 0; i <= score.length - M; i++) {
            // 计算区间 i - j 之间的最小值
            int min = score[i];
            for (int k = i; k <= i + M - 1; k++) {
                min = Math.min(min, score[k]);
            }
            ans.add(min);
        }

        for (int i = 0; i < ans.size(); i++) {
            System.out.print(ans.get(i));
            if(i != ans.size() - 1) {
                System.out.print(",");
            }
        }
    }

    // 单调队列解法
    private static void betterWay(int M, int[] score) {
        List<Integer> ans = new ArrayList<>();
        LinkedList<Integer> queue = new LinkedList<>();

        for (int i = 0; i < M; i++) {
            while (queue.size() > 0 && queue.getLast() > score[i]) {
                // 如果队列中最后一个元素比当前元素大,那么移除掉队列中的元素
                // 保证队列中第一个元素始终是最小的
                queue.removeLast();
            }
            queue.add(score[i]);
        }

        ans.add(queue.getFirst());

        for (int i = M; i < score.length; i++) {
            //  从下一个 区间开始
            if(score[i - M] == queue.getFirst()) {
                // i - M 是区间之外了,如果和队列中的第一个元素相等,那么证明需要将其移除
                queue.removeFirst();
            }

            // 判断当前元素
            while(queue.size() > 0 && queue.getLast() > score[i]) {
                // 如果当前元素比 队列末尾的元素还要小,那么移除队列末尾元素
                queue.removeLast();
            }

            queue.add(score[i]);

            ans.add(queue.getFirst());
        }

        for (int i = 0; i < ans.size(); i++) {
            System.out.print(ans.get(i));
            if(i != ans.size() - 1) {
                System.out.print(",");
            }
        }
    }

}