POJ 2823 Sliding Window(优先队列)
简单来说,优先队列的构造用一位数组就可以实现。通过特定限制条件,伴随着每一个数据的输入处理数组。
以此题为例,获取每一个子区间内的最小值,一个简单方法就是构造一个单调递增的数列,并且不断维持这个数列。显然,在这个数列下,要获取每个子区间的最小值自然是直接取这个数列的第一个元素即可。那么,如何维持这个数列呢?
以POJ原数据为例,总区间段长度为8,子区间段长度为3
以求最小值为例
1 3 -1 -3 5 3 6 7
读入数据1,之前数列为空,保留数据1为数列的第一个元素,此时区间最小值无意义,因为尚未遍历三个数据。
读入数据3,之前数列含有1,保留数据3为数列的第二个元素,因为虽然1比3小,但是1离开数组的时间早。此时区间最小值无意义,因为尚未遍历三个数据。
读入数据-1,之前数列为有1,3,将-1同3相比较,3比-1大,且离开时间比-1早,故3接下来永远不可能成为区间最小值,删除3。注意这里数组为一般线性存储空间,即-1取代了3的位置。现在数组中为1,-1。因为-1前面还有1,1同-1相比较,1比-1大,且离开时间比-1早,故3接下来永远不可能成为区间最小值,删除1。此时数组中只有-1。而且此时已经遍历三个数据,直接读数组第一个数据-1即为第一个子区间的最小值。
读入数据-3,之前数列为只有-1,将-1同-3相比较,-1比-3大,且离开时间比-3早,故-1接下来永远不可能成为区间最小值,删除-1。此时数组中只有-3。直接读数组第一个数据-3即为第二个子区间的最小值。
读入数据5,之前数列为只有-3,将5同-3相比较,5比-3大,但-3离开时间比5早,故5有可能成为接下来的区间最小值。此时数组中有两个元素,-3,5。此时在判断最小值前需要判断-3是否合法,即此时-3理论上是否还处在队列中,答案是合法,故此时第三个子区间最小值即为-3。
后面变化规律均一致,不再重复分析。
这里补充一种样例没有反映出的特殊情况。还是三个数据为一个子区间段,求最小值,假设输入为1,2,3,4,5,6。
显然,第一个子区间段最小值为1,这个数组中存在的值有1,2,3
当下一个数据4读入时,则需要将4放在3的后面。同时因为数组中有多个值,需要判断1是否合法。结果1不合法,这就导致了需要将1舍弃。然而,在线性存储单元中,比较快速的方法就是将头的指向整体向右移动。
下面简单介绍此题的算法。
此题中的get_min函数,假设子序列区间长度为k,定义子区间数组的第一个元素为head = 1,最后一个元素tail = 0;长度为 (tail - head)
首先按顺序将前(k-1)读入,接下来维护这个数组,最终输出。
#include <iostream>
#include <fstream>
using namespace std;
#define MAX 1000010
int A[MAX];
int Q[MAX];
int P[MAX];
int Min[MAX];
int Max[MAX];
int n,k;
void get_min()
{
int head=1,tail=0;
for(int i=0; i<k-1; i++)
{
while(head<=tail && Q[tail]>=A[i])
--tail;
Q[++tail]=A[i];
P[tail]=i;
}
for(int i = k-1; i<n; i++)
{
while(head<=tail && Q[tail]>=A[i])
--tail;
Q[++tail]=A[i];
P[tail]=i;
while(P[head]<i-k+1)
head++;
Min[i-k+1]=Q[head];
}
}
void get_max()
{
int head=1,tail=0;
for(int i=0; i<k-1; i++)
{
while(head<=tail && Q[tail]<=A[i])
--tail;
Q[++tail]=A[i];
P[tail]=i;
}
for(int i = k-1; i<n; i++)
{
while(head<=tail && Q[tail]<=A[i])
--tail;
Q[++tail]=A[i];
P[tail]=i;
while(P[head]<i-k+1)
head++;
Max[i-k+1]=Q[head];
}
}
void output()
{
for(int i=0; i<n-k+1; i++)
cout << Min[i] << " ";
cout << endl;
for(int i=0; i<n-k+1; i++)
cout << Max[i] << " ";
cout << endl;
}
int main()
{
int i;
scanf("%d%d",&n,&k);
for(i=0; i<n; i++)
{
scanf("%d",&A[i]);
}
get_min();
get_max();
output();
return 0;
}