POJ2823_Sliding Window

 以前也碰到过这种类型的题目,以前好像做出来过,但是忘记了,这次又坑了。

题目很简单,对于从前到后每一个连续的长度为k的数字,求出这段数字中的最大的数字和最小的数字。

一开始我用离散化+树状数组来更新和查询,时间复杂度为n*log(n)*log(n)。这尼玛果断T啊。

后来才发现,没那么复杂哦。其实是用一个优先队列来解决问题的。

什么意思呢?对于初始状态,我们可以加入k-1个元素,然后每次加一个元素,进行弹出和查询的操作。

首先我们创建两个队列,分别用来解决最大值和最小值的问题。

这里只需要说明一下最小值的队列就可以了,最大值队列同理。

假如当前我们要加入一个新的元素x,我们只需要将x与队尾的元素进行比较,如果我们发现x小于队尾的元素,那么显然队尾的那个元素是没有用的,可以直接删除。为什么?因为对于当前加入的x,接下来的所有询问中,有队员元素在的询问一定有x在,也就是说无论如何队尾元素都不会是最小值,我们可以直接删除。

有了上面的这条性质,我们就可以发现这个优先队列的性质就是前面的数字总是小于后面的数字,所以。。。。

查询的时候,最小的数字就是队首的元素。

接下来还有一个删除的操作,这个更加简单,我们只要判断队首的元素与当前加入的这个数的位置,看看差值是不是大于或等于k就可以决定这个数是不是需要删除了。

 1 #include <cstdio>
 2 #define M 1000005
 3 using namespace std;
 4 
 5 int q1[M],q2[M],H1,H2,T1,T2;
 6 int a[M],n,k;
 7 int ans1[M],ans2[M];
 8 
 9 int main()
10 {
11     scanf("%d%d",&n,&k);
12     H1=H2=1;
13     T1=T2=0;
14     for (int i=1; i<=n; i++)
15     {
16         scanf("%d",&a[i]);
17         while (a[q1[T1]]>=a[i] && T1>=H1) T1--;
18         while (a[q2[T2]]<=a[i] && T2>=H2) T2--;
19         q1[++T1]=q2[++T2]=i;
20         if (q1[H1]+k<=i) H1++;
21         if (q2[H2]+k<=i) H2++;
22         ans1[i]=a[q1[H1]],ans2[i]=a[q2[H2]];
23     }
24     printf("%d",ans1[k]);
25     for (int i=k+1; i<=n; i++) printf(" %d",ans1[i]);
26     printf("
%d",ans2[k]);
27     for (int i=k+1; i<=n; i++) printf(" %d",ans2[i]);
28     printf("
");
29 }