POJ - 2823 - Sliding Window (单调栈)

An array of size n ≤ 10 6 is given to you. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves rightwards by one position. Following is an example:
The array is [1 3 -1 -3 5 3 6 7], and k is 3.

Window positionMinimum valueMaximum value
[1 3 -1] -3 5 3 6 7-13
1 [3 -1 -3] 5 3 6 7-33
1 3 [-1 -3 5] 3 6 7-35
1 3 -1 [-3 5 3] 6 7-35
1 3 -1 -3 [5 3 6] 736
1 3 -1 -3 5 [3 6 7]37

Your task is to determine the maximum and minimum values in the sliding window at each position.

Input
The input consists of two lines. The first line contains two integers n and k which are the lengths of the array and the sliding window. There are n integers in the second line.
Output
There are two lines in the output. The first line gives the minimum values in the window at each position, from left to right, respectively. The second line gives the maximum values.
Sample Input

8 3
1 3 -1 -3 5 3 6 7

Sample Output

-1 -3 -3 -3 3 3
3 3 5 5 6 7

题目解释
给出一行n个数字,给定区间长度为k,每次向右移动一个单位,记录此移动区间中的最小值和最大值。输出分两行,第一行输出移动过程中的最小值,第二行输出移动过程中的最大值。
题解思路
 这个题目要是想用暴力直接做的话,肯定是过不了的,一旦k很大,那么每次移动就要耗费大量的时间。那么我们可以维护一个单调栈来保存区间的状态。(以下解释均以求最小值为例,求最大值反之即可
 为了维护区间的最小值,我们需要维护一个单调递增的单调栈,以确保栈底(即单调栈数组的最左端)元素一定是区间最小的(即区间最优值)。这样进入一个新的区间,我们只需要确保两件事:

  • 加入新元素后区间长度为k
  • 单调栈的单调性

 这两件事也就是代码中的两个while循环所做的事。
 这里可以打一个比方,单调栈从前到后就是历代帝王。head指向老一代帝王,tail指向最新一代帝王(当然head没有退位的话 tail 也没有用)。单调栈维护单调递增的序列,求最小值,也就是帝王中值越小越厉害。当老一代帝王发现自己已经落后于时代(区间宽度大于k)就会宣布退位(head++,缩小区间大小至k),这个时候,紧随其后的帝王继位(成为当前区间中最小值);一个新的帝王之后到来之后,需要看一下之前的历代帝王的能力,如果自己更厉害(即值更小),那么完全可以依次顶替前面帝王的位置(即第二个循环,把比自己值更大的依次弹出栈,tail–),否则就只能乖乖的排在后面(que[++tail] = a[i]),然后等待他前面的一任退位。

AC代码:

#include<cstdio>
using namespace std;

const int maxn = 1000010;
int n,k,head,tail;
int a[maxn];
int que[maxn];	// 保存单调栈的数组,只是名字而已,stack为名也是一样的
int label[maxn];	// 单调栈中位置=>原数组的下标,一个映射关系

int main()
{
	scanf("%d%d", &n, &k);
	for (int i = 1;i <= n; i++)
		scanf("%d", &a[i]);
	head = 1;
	tail = 0;
	for (int i = 1; i <= n; i++)
	{
		// i为当前元素位置,label为单调栈元素在原数组中的位置
		// 该while循环用于确保新区间(不包含第i个元素)长度为k-1
		while (head <= tail && i-label[head] >= k) head++;
		// 维护一个单调递增的单调栈
		while (head <= tail && que[tail] >= a[i]) tail--;
		que[++tail] = a[i];
		label[tail] = i;
		if (i >= k) printf("%d ", que[head]);
	}
	printf("\n");
	head = 1;
	tail = 0;
	for (int i = 1; i <= n; i++)
	{
		while (head <= tail && i-label[head] >= k) head++;
		while (head <= tail && que[tail] <= a[i]) tail--;
		que[++tail] = a[i];
		label[tail] = i;
		if(i >= k) printf("%d ", que[head]);
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值