绝对差不超过限制的最长连续子数组 RMQ+二分答案

感谢https://www.cnblogs.com/yyxayz/p/4109390.html


思路,首先暴力肯定超时了,那么就要想办法来优化,当然是百度了
首先很显然的是,如果一个区间的最大值和最小值分别是mi和ma
那么如果ma-mi<=limits,那么这个区间就是合法的区间
所以我们就要找一个方法快速的求区间的最大值与最小值
这当然可以用线段树来做,但是线段树写起来麻烦,此时祭出
大杀器,rmq,可以通过o(nlogn)复杂度来预处理,o(1)复杂度
查询区间的最值,有了这个之后,就可以二分答案,o(nlogn)来求解了
但是感觉双指针在这里应该也能做到,而且是o(n)但是影响不大了


RMQ的基本思想

RMQ的基本思想就是对区间的进行拆分,来利用动态规划的方法求解子问题
利用一个dp数组,其中dp[i][j]表示以i开始,长度为2^j次方的数组的最值
可以知道,这个数组是可以覆盖任何一个子区间的,因为数组的长度为n,那么
n必然可以拆分成2^k这种的相加形式,而且因为我们查询的时候,区间可以覆盖
所以一次查询只需要访问两次数组。而前面的预处理过程则由,大区间有小区间
覆盖来求得,方便快捷


int maxn[100005][20];
int minn[100005][20];
class Solution {
public:
    int longestSubarray(vector<int>& nums, int limit) {
        //二分答案,求最大值与最小值
        nums.insert(nums.begin(),0);
        getbestarr(nums.size()-1,nums);
        int ma = nums.size()-1;
        int mi=1;
        int mid=0;
        int ans = 0;
        int len=nums.size()-1;
        while(mi<=ma){
            bool flag = false;
            mid = (ma+mi)>>1;
            for(int i=1; i<=len-mid+1;i++){
                if(abs(query(i,i+mid-1,0)-query(i,i+mid-1,1))<=limit){
                    flag=true;
                    ans = mid;
                    break;
                }
            }
            if(flag){
                mi=mid+1;
            }
            else{
                ma=mid-1;
            }
        }
        return ans;
    }
    void ini(){
        for(int i=0; i<100005; i++){
            for(int j=0; j<20; j++)
                  minn[i][0]= maxn[i][0] = 0;  
        }
    }
    void getbestarr(int n,vector<int>&arr)//n为给定的数组的长度  
    {  
        ini();
         int tem = (int)floor(log2((double)n));
       for(int i=1;i<=n;i++)  
            minn[i][0]= maxn[i][0] = arr[i];  
        for(int j=1;j<=tem;j++) //下标从1开始  
             for(int i=1;i+(1<<j)-1<=n;i++)  
           {  
                 maxn[i][j] = max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);  //最大值  
                 minn[i][j] = min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);  //最小值  
            }  
    }
    int query(int a,int b,bool getwhat)//getwhat表示你是想取最大还是最小  
    {  
       int k = log2(b-a+1);  
       if(getwhat)  
           return max(maxn[a][k],maxn[b-(1<<k)+1][k]);  
       else  
             return min(minn[a][k],minn[b-(1<<k)+1][k]);  
    }
};
posted @ 2020-05-03 12:11  CrosseaLL  阅读(236)  评论(0编辑  收藏  举报