附上题目链接:点击这里
题意:
给你一个长度为 n 的数组 , 再给你一个只能看见 k 个数字的窗口 , 现在把这个窗口放在数组上从左到右依次滑动 , 求出每滑动一次时窗口可见的数字中的最大值和最小值。
注意这一题的数据范围是1e6 , 因此暴力是不可能的 ;
这时要用到单调队列。(就是维护一个单调的序列)
举个例子:
数列为:6 4 10 10 8 6 4 2 12 14
N=10,K=3;
那么我们构造一个长度为3的单调递减队列:
首先,那6和它的位置0放入队列中,我们用(6,0)表示,每一步插入元素时队列中的元素如下
插入6:(6,0);
插入4:(6,0),(4,1);
插入10:(10,2);
插入第二个10,保留后面那个:(10,3);
插入8:(10,3),(8,4);
插入6:(10,3),(8,4),(6,5);
插入4,之前的10已经超出范围所以排掉:(8,4),(6,5),(4,6);
插入2,同理:(6,5),(4,6),(2,7);
插入12:(12,8);
插入14:(14,9);
那么f(i)就是第i步时队列当中的首元素:6,6,10,10,10,10,8,6,12,14
同理,最小值也可以用单调队列来做。
大致就是这样的一个思路 ,下面代码?(唉~感觉自己可敷衍了 , 对不起今晚吃的肉和雪碧)
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
const int maxn = 1010000;
using namespace std;
int a[maxn] , mx[maxn] , mn[maxn] , n , m;
struct node
{
int x , y;//值与下标
}v[maxn];
void getmax()//下标从1 开始
{
int head = 1 , tail = 0;
for(int i = 1 ; i < m ; i++)
{
while(head <= tail && v[tail].x < a[i]) tail--;
v[++tail].x = a[i];v[tail].y = i;
}
for(int i = m ; i <= n ; i++)
{
while(head <= tail && v[tail].x < a[i]) tail--;
v[++tail].x = a[i];v[tail].y = i;
while(v[head].y < (i-m+1)) head++;
mx[i-m+1] = v[head].x;
}
}
void getmin()
{
int head = 1 , tail = 0;
for(int i = 1 ; i < m ; i++)
{
while(head <= tail && v[tail].x >= a[i]) tail--;
v[++tail].x = a[i];v[tail].y = i;
}
for(int i = m ; i <= n ; i++)
{
while(head <= tail && v[tail].x >= a[i]) tail--;
v[++tail].x = a[i];v[tail].y = i;
while(v[head].y < (i-m+1))head++;
mn[i-m+1] = v[head].x;
}
}
int main()
{
scanf("%d %d" , &n , &m);
for(int i = 1 ; i <= n ; i++)
{
scanf("%d" , &a[i]);
}
getmin();
getmax();
for(int i = 1 ; i <= n-m+1 ; i++)
{
if(i==1)printf("%d",mn[i]);
else printf(" %d",mn[i]);
}
printf("\n");
for(int i = 1 ; i <= n-m+1 ; i++)
{
if(i==1)printf("%d",mx[i]);
else printf(" %d",mx[i]);
}
printf("\n");
return 0;
}