题目描述
- 公司准备对他下面的个产品评选最差奖
- 评选的方式是首先对每个产品进行评分,然后根据评分区间计算相邻几个产品中最差的产品
- 评选的标准是依次找到从当前产品开始前个产品中最差的产品,请给出最差产品的评分序列。
输入描述
- 第一行,数字,表示评分区间的长度,取值范围是
- 第二行,产品的评分序列,比如
[12, 3, 8, 6, 5]
,产品数量N
的范围是
输出描述
- 评分区间内最差产品的评分序列
用例
--输入
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(",");
}
}
}
}