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.
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;
}