hdu3530 Subsequence

Problem Description
There is a sequence of integers. Your task is to find the longest subsequence that satisfies the following condition: the difference between the maximum element and the minimum element of the subsequence is no smaller than m and no larger than k.
 

Input
There are multiple test cases.
For each test case, the first line has three integers, n, m and k. n is the length of the sequence and is in the range [1, 100000]. m and k are in the range [0, 1000000]. The second line has n integers, which are all in the range [0, 1000000].
Proceed to the end of file.
 

Output
For each test case, print the length of the subsequence on a single line.
 

Sample Input
5 0 0 1 1 1 1 1 5 0 3 1 2 3 4 5
 

Sample Output
5

4

这题我用的是rmq算法,先初始化2的次方的区间最大值最小值,然后循环算出最大的区间长度。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<map>
#include<string>
using namespace std;
int a[100006];
int minx[100100][30];
int maxx[100100][30];
void init_rmq(int n)
{
	int i,j;
	for(i=1;i<=n;i++)maxx[i][0]=minx[i][0]=a[i];
	for(j=1;j<=20;j++){
		for(i=1;i<=n;i++){
			if(i+(1<<j)-1<=n)
			{maxx[i][j]=max(maxx[i][j-1],maxx[i+(1<<(j-1))][j-1]);
			minx[i][j]=min(minx[i][j-1],minx[i+(1<<(j-1))][j-1]);}
		}
	}
}

int getmax(int l,int r)
{
	int k,i;
	if(l>r)swap(l,r);
	k=(log((r-l+1)*1.0)/log(2.0));
	return max(maxx[l][k],maxx[r-(1<<k)+1][k]);
}

int getmin(int l,int r)
{
	int k,i;
	if(l>r)swap(l,r);
	k=(log((r-l+1)*1.0)/log(2.0));
	return min(minx[l][k],minx[r-(1<<k)+1][k]);
}

int main()
{
	int n,m,i,j,k,l,r,ans,t;
	while(scanf("%d%d%d",&n,&m,&k)!=EOF)
	{
		for(i=1;i<=n;i++){
			scanf("%d",&a[i]);
		}
		init_rmq(n);
		l=1;ans=0;           //l不用每次从1开始判,因为之前的l越小,r-l+1的值就越大。
		for(i=1;i<=n;i++){
			r=i;
			if(l>r)continue;
			while(getmax(l,r)-getmin(l,r)>k)l++; //如果大于k,那么后面的r对于此时的l肯定不满足不大于k,所以必须l++;
			if(getmax(l,r)-getmin(l,r)>=m && getmax(l,r)-getmin(l,r)<=k)ans=max(ans,r-l+1);
		}
		printf("%d\n",ans);
	}
	return 0;
}<pre name="code" class="cpp">


今天又用单调队列做了一下,想了很长时间,一开始一直想不明白。先设两个单调队列q1,q2分别记录最大值和最小值,设定一个起始点last,之后last=0只能不断向后移动,每加入一个数,判断q2[front2][0]-q1[front1][0]的值是不是大于k,如果大于那么移除q1[front1][1].q2[front2][1]中较小的(这里我差不多想了一个晚上(汗),我一直搞不懂为什么每次都要去掉序号排在前面的,后来想了想发现因为序列是连续的,如果先去掉的是序号大,那么序号小的必定会去掉,和时间的原理差不多),然后每次last记录min(q1[front1][1],q2[front2][1]),等到一个合适的值,再判断。

</pre><pre name="code" class="cpp">
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<map>
#include<string>
#define maxn 100005
#define inf 88888888
using namespace std;
int a[maxn],q1[maxn][2],q2[maxn][2];
int main()
{
	int n,m,i,j,k,front1,rear1,front2,rear2,last,num;
	while(scanf("%d%d%d",&n,&m,&k)!=EOF)
	{
		for(i=1;i<=n;i++)scanf("%d",&a[i]);
		num=0;last=0;     //last从0开始 
		front1=front2=1;rear1=rear2=0;
		for(i=1;i<=n;i++){
			while(front1<=rear1 && q1[rear1][0]>=a[i])rear1--;   //不加也行 
			rear1++;
			q1[rear1][0]=a[i];q1[rear1][1]=i;
			while(front2<=rear2 && q2[rear2][0]<=a[i])rear2--;
			rear2++;
			q2[rear2][0]=a[i];q2[rear2][1]=i;
			//last=min(q1[front1][1],q2[front2][1]);这里不能加,会错,因为如果上一循环中的last比min(q2[front2][1],q1[front1][1])小,但last满足m<=q2[front2][0]-q1[front1][0]<=k,结果就缩小了。 
			while(front1<=rear1 && front2<=rear2 && q2[front2][0]-q1[front1][0]>k){
				if(q2[front2][1]>q1[front1][1])last=q1[front1++][1];
				else last=q2[front2++][1];
			}
			if(q2[front2][0]-q1[front1][0]>=m){
				if(i-last>num)num=i-last;
			}
		}
		printf("%d\n",num);
	}
	return 0;
}

又用线段树的方法写了一下,1A感觉很不错(笑),感觉三种方法的原理是一样的。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<map>
#include<string>
using namespace std;
#define maxn 100005
#define inf 88888888
int maxx,minx,minzuo,maxzuo;
int a[maxn];
struct node{
	int l,r,max,min,minzuo,maxzuo;
}b[4*maxn];
void build(int l,int r,int i)
{
	int mid;
	b[i].l=l;b[i].r=r;
	if(l==r){
		b[i].max=a[l];b[i].min=a[l];
		b[i].maxzuo=b[i].minzuo=l;
		return;
	}
	mid=(l+r)/2;
	build(l,mid,i*2);
	build(mid+1,r,i*2+1);
	if(b[i*2].max>b[i*2+1].max){
		b[i].max=b[i*2].max;
		b[i].maxzuo=b[i*2].maxzuo;
	}
	else{
		b[i].max=b[i*2+1].max;
		b[i].maxzuo=b[i*2+1].maxzuo;
	}
	
	if(b[i*2].min<b[i*2+1].min){
		b[i].min=b[i*2].min;
		b[i].minzuo=b[i*2].minzuo;
	}
	else{
		b[i].min=b[i*2+1].min;
		b[i].minzuo=b[i*2+1].minzuo;
	}
}

void question(int l,int r,int i)
{
	int mid;
	if(b[i].l==l && b[i].r==r){
		if(maxx<b[i].max){
			maxx=b[i].max;maxzuo=b[i].maxzuo;
		}
		if(minx>b[i].min){
			minx=b[i].min;minzuo=b[i].minzuo;
		}
		return;
	}
	mid=(b[i].l+b[i].r)/2;
	if(r<=mid)question(l,r,i*2);
	else if(l>mid)question(l,r,i*2+1);
	else{
		question(l,mid,i*2);
		question(mid+1,r,i*2+1);
	}
}

int main()
{
	int n,m,i,j,k,last,num;
	while(scanf("%d%d%d",&n,&m,&k)!=EOF)
	{
		for(i=1;i<=n;i++)scanf("%d",&a[i]);
		build(1,n,1);
		last=0;num=0;maxzuo=minzuo=0;
		for(i=1;i<=n;i++){
			while(1){
				maxx=-inf;minx=inf;
				if(last>=i)break;
				question(last+1,i,1);
				if(maxx-minx>k){
				   if(minzuo<maxzuo)last=minzuo;
   				   else last=maxzuo;
				}
				else break;
			}
			if(maxx-minx>=m && last<i){
				if(num<i-last)num=i-last;
			}
		}
		printf("%d\n",num);
	}
	return 0;
}



posted @ 2015-05-16 11:09  Herumw  阅读(184)  评论(0编辑  收藏  举报