【题解】CSU1553 Good subsequence

这是今年暑假的三校联赛中遇到的一道题目,也是一道明显的区间处理题目,开始的时候只想到了用线段树来进行储存,没想到在时间被卡了(其实也是自己偷懒了,查找的时候懒得想办法,暴力去查,效率太低了)。
同时这道题也让我有了尺取法这个概念,虽然不是什么很难的方法,很多人在无意中就会写出来,但是有了一个这样的明确的概念以后更加能在题目中有意识的去运用这个针对区间处理问题的枚举方法。


题目

原题链接:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1553

1553: Good subsequence
Time Limit: 2 Sec
Memory Limit: 256 Mb

Give you a sequence of n numbers, and a number k you should find the max length of Good subsequence. Good subsequence is a continuous subsequence of the given sequence and its maximum value - minimum value<=k. For example n=5, k=2, the sequence ={5, 4, 2, 3, 1}. The answer is 3, the good subsequence are {4, 2, 3} or {2, 3, 1}.

Input
There are several test cases.
Each test case contains two line. the first line are two numbers indicates n and k (1<=n<=10,000, 1<=k<=1,000,000,000). The second line give the sequence of n numbers a[i] (1<=i<=n, 1<=a[i]<=1,000,000,000).
The input will finish with the end of file.

Output
For each the case, output one integer indicates the answer.

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

Sample Output
3
1


题目大意

给出一个序列的长度n以及一个限定值k,然后依次输入其中的n个元素,求:

该序列的一个子序列,该子序列需要满足条件:最大元素max-最小元素min <= 限定值k
且长度为满足条件的子序列中最大,输出该长度。


解题思路

最近正好在看各种线段树的处理,既然是区间处理问题,那么这道题我们就用线段树来存储数据(迫真)。
题目中要求用到最大值与最小值的差,因此这里用两颗线段树分别储存相应区间的最大值与最小值
然后,因为是要找到长度最长的符合条件的子序列,最直观、最省事的办法自然是从长度最长的子序列向长度最短开始枚举了,但是,正如前言所说的,直接用暴力枚举的方法去找符合条件的会导致超时,因此需要在枚举区间上做出一些优化,这里采用尺取法进行区间枚举。


尺取法

尺取法是针对区间枚举的一种方法,大致过程就是通过从前到后不断的推移左右子区间边界找到合适的区间——

先将l(子区间左边界)与r(子区间右边界)均置于原区间最左端,然后r开始右移,如果当前区间满足条件,
则将其与进行记录的中间变量进行比较和替换,r继续右移到区间不满足条件时,将l右移,r重置并重
复上述的过程。

举个例子来说明,从{5,4,2,3,1}这个序列中找到符合上述题意的子序列,k为2,尺取法的枚举过程如下:

5 4 2 … … Maxlength为2
… 4 2 3 1 Maxlength为3
… … 2 3 1 Maxlength为3
… … … 3 1 Maxlength为3
… … … … 1 Maxlength为3

可以看出,尺取法仅适用于具有一定规律、在不满足条件之后的子序列之后不会再出现满足条件的子序列的情况的序列
比如当l为0的时候的第一遍枚举,在r到达2时停下了,因为此时的子序列为{5,4,2},max为5,min为2,其差值为3,大于k,此后的子序列只会使这个差值不断增大,因此之后的子序列都没有枚举的必要。


解题代码

import java.util.Scanner;

public class Main {
	
    static int t[] = new int[101000<<2];
    static int y[] = new int[101000<<2];
    static int a[] = new int[101000];
    static int n,k;
    
    static int max(int a,int b) { return a>b ? a : b; }
    static int min(int a,int b){return a>b? b : a;}
    
    static void up1(int rt) {
        t[rt] = max(t[rt<<1],t[rt<<1|1]);
    }//更新最大值线段树
    
    static void up2(int rt) {
        y[rt] = min(y[rt<<1],y[rt<<1|1]);
    }//更新最小值线段树
    
    static void build_u(int l,int r,int rt) {
        if (l==r) {
            t[rt] = a[l]; return ;
        }
        int mid = l+r>>1;
        build_u(l,mid,rt<<1);
        build_u(mid+1,r,rt<<1|1);
        up1(rt);
    }//构建最大值线段树
    
    static void build_d(int l,int r,int rt) {
        if (l==r) {
            y[rt] = a[l]; return ;
        }
        int mid = l+r>>1;
        build_d(l,mid,rt<<1);
        build_d(mid+1,r,rt<<1|1);
        up2(rt);
    }//构建最小值线段树
    
    static int query_u(int L,int R,int l,int r,int rt) {
        if (L<=l && r<=R) return t[rt];
        int mid = l+r>>1;
        int ret = 0;
        if (L<=mid) ret = max(ret,query_u(L,R,l,mid,rt<<1));
        if (R> mid) ret = max(ret,query_u(L,R,mid+1,r,rt<<1|1));
        return ret;
    }//查找相应区间最大值
    
    static int query_d(int L,int R,int l,int r,int rt) {
	    if (L<=l && r<=R)return y[rt];
	    int mid = l+r>>1;
	    int ret = Integer.MAX_VALUE;
	    if (L<=mid) ret = min(ret,query_d(L,R,l,mid,rt<<1));
	    if (R> mid) ret = min(ret,query_d(L,R,mid+1,r,rt<<1|1));
	    return ret;
    }//查找相应区间最小值
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            n = sc.nextInt(); k = sc.nextInt();
            for (int i = 1; i <= n; i ++) a[i] = sc.nextInt();
            build_u(1,n,1);
            build_d(1,n,1);
            //尺取法枚举区间
            int MAX = -1;
            int maxv =a[1], minv = a[1];
            int l = 1,r = 1;
            while(l<=n){
                while(r<=n){
                    maxv = query_u(l,r,1,n,1);
                    minv = query_d(l,r,1,n,1);
                    if(maxv-minv>k) break;
                    MAX = max(r-l+1,MAX);
                    r++;
                }
                l++;
                maxv = query_u(l,r,1,n,1);
                minv = query_d(l,r,1,n,1);
            }
           System.out.println(MAX);
        }
    }

}

猜你喜欢

转载自blog.csdn.net/Jourofant/article/details/107496636