被逼着写的一个题,以前觉得难度很大的东西,一旦有动力去好好学习,其实真的并没有那么难

ACM代码其实没有多少行,而且原理也就那么几句,怀着一颗向上的心,每天正能量,每天一道a+b,时间长了真的会有很大进步的

因为,时间都看得见

(废话了一波)


题目链接:POJ2823

题意:给定n,k,n个数,求每个连续的以K为区间长度的区间中的最小值和最大值

方法1:线段树!区间内的最大最小值,果断线段树搞

(不明白为啥只能用C++提交,G++会超时)

我对于这个的理解是:C++的很多实现较少,而且编译不稳定所以快(追求效率用C++提交)

G++很多东西实现得更好,或者说编译原理不相同,速度虽然慢点,但是有的题用G++能过C++一直wa的


代码就不解释了,直接贴:

int n,k;
int Min[maxn<<2];
int Max[maxn<<2];

void build(int rt,int l,int r){
	if (l==r){
		scanf("%d",&Min[rt]);
		Max[rt]=Min[rt];
		return;
	}
	int mid=(l+r)>>1;
	build(lson);
	build(rson);
	Min[rt]=min(Min[ll],Min[rr]);
	Max[rt]=max(Max[ll],Max[rr]);
	return;
}

int getMin(int L,int R,int rt,int l,int r){
	if (l>=L&&R>=r) return Min[rt];
	int mid=(l+r)>>1;
	if (R<=mid) return getMin(L,R,lson);
	else if (L>mid) return getMin(L,R,rson);
	return min(getMin(L,R,lson),getMin(L,R,rson));
}

int getMax(int L,int R,int rt,int l,int r){
	if (l>=L&&R>=r) return Max[rt];
	int mid=(l+r)>>1;
	if (R<=mid) return getMax(L,R,lson);
	else if (L>mid) return getMax(L,R,rson);
	return max(getMax(L,R,lson),getMax(L,R,rson));
}

int main(){
	//input;
	while(scanf("%d%d",&n,&k)!=EOF){
		build(1,1,n);
		for(int i=1;i<=n-k+1;i++)
			printf("%d%c",getMin(i,i+k-1,1,1,n),i==n-k+1?'\n':' ');
		for(int i=1;i<=n-k+1;i++)
			printf("%d%c",getMax(i,i+k-1,1,1,n),i==n-k+1?'\n':' ');
	}
	return 0;
}

方法2:单调队列

网上搜这个题,很多对于单调队列的解释和理解,毕竟算是个简单题

单调队列的意义:当前队列维护的是值的单调性,不是位置的单调性,意味着是个结构体,是按照值来sort的

任何一个数据结构,涉及到的最有意义的部分是插入和删除

以最小值为例:

添加时:准备添加时,把需要添加的元素值不断的和队尾的元素值比较,直到队尾的元素小于等于需要添加的值或者队列为空了

然后把该元素添加进去

比如:当前值是3,2,6,10,5(现在还没有管位置)

第一轮,队列为【3】

第二轮,3出队因为比2大,队列为【2】

第三轮,6进队,队列为【2,6】

第四轮,10进队,队列为【2,6,10】

第五轮,5进队之前先得让6和10出队,队列为【2,5】

添加的时候,是为了达到查询时:最小的元素一定在队头这个目的


删除:删除这个操作是要根据题意来搞的

比如这个题要求的是区间长度最大为K,那么需要用结构体记录每次插入元素的值和位置,两个差值需要小于K


懂了原理之后剩下的见代码(单调队列的时间确实比线段树省)

求最小的单调队列解释得很清楚,最大的同理

struct node{
	int pos,val;
}q[maxn];

int num[maxn],n,k;
int Min[maxn];
int Max[maxn];

void getMin(){
	int head=1,tail=0;
	for(int i=1;i<k;i++){
		//前k-1个数跟查询无关,直接放进来 
		while(head<=tail&&num[i]<q[tail].val) tail--;
		//插入的准备操作,要么队列为空,要么找到一个位置使得符合单调队列定义 
		tail++;
		q[tail].val=num[i];
		q[tail].pos=i;
		//插入 
	}
	for(int i=k;i<=n;i++){
		while(head<=tail&&num[i]<q[tail].val) tail--;
		tail++;
		q[tail].val=num[i];
		q[tail].pos=i;
		while(i-q[head].pos>=k) head++;
		//删除操作,维护最大的区间长度 
		Min[i-k]=q[head].val;
		//答案记录 
	}
}

void getMax(){
	int head=1,tail=0;
	for(int i=1;i<k;i++){
		while(head<=tail&&num[i]>q[tail].val) tail--;
		tail++;
		q[tail].val=num[i];
		q[tail].pos=i;
	}
	for(int i=k;i<=n;i++){
		while(head<=tail&&num[i]>q[tail].val) tail--;
		tail++;
		q[tail].val=num[i];
		q[tail].pos=i;
		while(i-q[head].pos>=k) head++;
		Max[i-k]=q[head].val;
	}
}

int main(){
	//input;
	while(scanf("%d%d",&n,&k)!=EOF){
		for(int i=1;i<=n;i++) scanf("%d",&num[i]);
		getMin();
		for(int i=0;i<=n-k;i++) printf("%d%c",Min[i],i==n-k?'\n':' ');
		getMax();
		for(int i=0;i<=n-k;i++) printf("%d%c",Max[i],i==n-k?'\n':' ');
	}
	return 0;
}


方法3:用STL的priority_queue来实现,上代码就很好理解了

int a[maxn],Min[maxn],Max[maxn],n,k;

struct cmp1{
	bool operator() (const int a1,const int a2){
		return a[a1]>a[a2];
	}
};

struct cmp2{
	bool operator() (const int a1,const int a2){
		return a[a1]<a[a2];
	}
};

priority_queue<int,vector<int>,cmp1> q1;
priority_queue<int,vector<int>,cmp2> q2;

int main(){
	//input;
	while(scanf("%d%d",&n,&k)!=EOF){
		int i,cnt1=0,cnt2=0;
		for(i=0;i<n;i++) scanf("%d",&a[i]);
		while(!q1.empty()) q1.pop();
		while(!q2.empty()) q2.pop();
		for(i=0;i<k;i++){
			q1.push(i);
			q2.push(i);
		}
		Min[cnt1++]=a[q1.top()];
		Max[cnt2++]=a[q2.top()];
		for(i=k;i<n;i++){
			q1.push(i);
			q2.push(i);
			while(i-q1.top()>=k) q1.pop();
			while(i-q2.top()>=k) q2.pop();
			Min[cnt1++]=a[q1.top()];
			Max[cnt2++]=a[q2.top()];
		}
		for(int i=0;i<=n-k;i++) printf("%d%c",Min[i],i==n-k?'\n':' ');
		for(int i=0;i<=n-k;i++) printf("%d%c",Max[i],i==n-k?'\n':' ');
	}
	return 0;
}