题目描述
假设塔子哥是一家电子产品公司的CEO,他的公司生产了 NNN 个产品。
由于市场竞争激烈,塔子哥决定评选出最差的产品,并对其进行改进。评选的方式是首先对每个产品进行评分,然后根据评分区间计算相邻几个产品中最差的产品。
评选的标准是依次找到从当前产品开始前 MMM 个产品中最差的产品,请给出最差产品的评分序列。
输入描述
第一行,数字 MMM ,表示评分区间的长度,取值范围是 0<M<100000<M<100000<M<10000
第二行,产品的评分序列,比如[12,3,8,6,5],产品数量N范围是 −10000<N<10000-10000 < N <10000−10000<N<10000
输出描述
评分区间内最差产品的评分序列
样例
输入1
3
12,3,8,6,5
输出1
3,3,5
输入2
4
12,1,5,3,8,6,5,9,8,6
输出2
1,1,3,3,5,5,5
题目思路
滑动窗口的基础题,思想就是用单调队列维护一个窗口,每次的答案就是队列的第一个
本题单调队列维护的是一个严格单调递增的队列
原理:试想,窗口为M大小在滑动时,每次的答案就是窗口内的最小值,但如果暴力判断的话时间复杂度将会是O(NM)。仔细思考我们可以用发现其实窗口内有些值是不用保存的,例如窗口大小为3,序列为[3, 4, 1, 2, ....],可以看到一开始的窗口覆盖的是[3, 4, 1],我们发现只要有1在,4是不可能再未来成为最差的那一个,3也是同理,因为有比他更差的,并且下标还是比他靠后的,固我们其实只要保存最小值1便可。再往后想,经过一次滑动后窗口覆盖的是[4, 1, 2],同理4是不用保存的,但2是有必要保存的,因为2虽然比1要大,但其下标却靠后,窗口在滑动时1离开了窗口后,2可能便是窗口的最小值,固对于这种情况,2是需要保存下来的,固此时需要保存[1,2]便可。如此分析可以看出单调队列维护的是一个严格单调递减的队列。
具体单调队列的实现是用双端队列,在队头取出当前窗口的最小值,同时如果说当前队头的最小值已经准备离开窗口则需要弹出队列,在队尾负责维护队列是单调的,即在队尾弹出比当前元素要大的值并且插入队伍
为了方便处理,单调队列存放的元素是下标,而不是值
该思路的时间复杂度为O(N)
C++代码
#include <algorithm>
#include <vector>
#include <deque>
#include <cstring>
#include <cstdio>
using namespace std;
int main() {
int M, val, N = 0;
scanf("%d\n", &M);
vector<int> a, ans;
deque<int> w;
while (~scanf("%d,", &val)) {
a.push_back(val);
N++;
}
for (int i = 0; i < N; i++) {
while (w.size() > 0 && a[w.back()] > a[i]) w.pop_back();
w.push_back(i);
if (i - w[0] == M) w.pop_front();
if (i >= M - 1) ans.push_back(a[w[0]]);
}
int flag = 0;
for (int x : ans) { // 按题目要求输出
if (flag == 0) flag = 1;
else printf(",");
printf("%d", x);
}
return 0;
}
Python代码
from collections import deque
M = int(input())
a = list(map(int, input().split(',')))
w = deque([])
ans = []
for i in range(len(a)):
while len(w) > 0 and a[w[len(w) - 1]] > a[i]:
w.pop()
w.append(i)
if i - w[0] == M:
w.popleft()
if i >= M - 1:
ans.append(a[w[0]])
# 按题目要求输出
flag = False
for x in ans:
if flag == False:
flag = not flag
else:
print(',', end = '')
print(x, end = '')
Java代码
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int M = scanner.nextInt();
String s = scanner.next();
String[] str = s.split(",");
int N = str.length;
int[] a = new int[N];
Deque<Integer> w = new LinkedList<Integer>();
List<Integer> ans = new ArrayList<>();
for (int i = 0; i < N; i++) {
a[i] = Integer.valueOf(str[i]).intValue();
while (!w.isEmpty() && a[w.getLast()] > a[i]) {
w.removeLast();
}
w.addLast(i);
if (i - w.getFirst() == M) w.removeFirst();
if (i >= M - 1) ans.add(a[w.getFirst()]);
}
Boolean flag = false; //按题目要求输出
for (int x : ans) {
if (flag == false) flag = true;
else System.out.print(',');
System.out.print(x);
}
}
}
Js代码
process.stdin.resume();
process.stdin.setEncoding('utf-8');
let input = '';
process.stdin.on('data', (data) => {
input += data;
return;
});
process.stdin.on('end', () => {
input = input.split('\n')
let M = input[0]
let a = input[1].split(',').map(Number)
let N = a.length
let ans = []
let w = []
for (let i = 0; i < N; i++) {
while (w.length > 0 && a[w[w.length - 1]] > a[i]) {
w.pop()
}
w.push(i)
if (i - w[0] == M) w.shift();
if (i >= M - 1) ans.push(a[w[0]]);
}
console(ans) // 自己解决按题意输出
})
653

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



