2407. 最长递增子序列 II-动态规划暴力解法和线段树

本文介绍了一种寻找最长递增子序列的算法,该子序列需满足严格递增且相邻元素差值不超过k的要求。提供了两种实现方法:动态规划暴力解法和使用线段树的分段哈希法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给你一个整数数组 nums 和一个整数 k 。

找到 nums 中满足以下要求的最长子序列:

子序列 严格递增
子序列中相邻元素的差值 不超过 k 。

请你返回满足上述要求的 最长子序列 的长度。

子序列 是从一个数组中删除部分元素后,剩余元素不改变顺序得到的数组。

示例 1:

输入:nums = [4,2,1,4,3,4,5,8,15], k = 3
输出:5
解释:
满足要求的最长子序列是 [1,3,4,5,8] 。
子序列长度为 5 ,所以我们返回 5 。
注意子序列 [1,3,4,5,8,15] 不满足要求,因为 15 - 8 = 7 大于 3 。

示例 2:

输入:nums = [7,4,5,1,8,12,4,7], k = 5
输出:4
解释:
满足要求的最长子序列是 [4,5,8,12] 。
子序列长度为 4 ,所以我们返回 4 。

示例 3:

输入:nums = [1,5], k = 1
输出:1
解释:
满足要求的最长子序列是 [1] 。
子序列长度为 1 ,所以我们返回 1 。

动态规划暴力解法如下:

int lengthOfLIS(int* nums, int numsSize, int k){
    int dp[numsSize];
    dp[0]=1;
    int max=1;
     for(int i=1;i<numsSize;i++){
         dp[i]=1;
         for(int j=0;j<i;j++){
             if(nums[i]-nums[j]<=k&&nums[i]>nums[j]){
                 
                   dp[i]=fmax(dp[j]+1,dp[i]);

             }
           
         }
         printf("%d ",dp[i]);
         max=fmax(dp[i],max);
     }
     return max;

}

线段树解法如下,但是下面这个线段是时无序线段树,比上面这个暴力解法好很多,但是还是时间上还是差了一些:
这题对于c语言求解不是很友好,一个不错的方法就是用分段哈希法,可以不错的解决这个问题:




struct segment_tree{
    struct tree *left;
    struct tree *right;
    int index;
    int max_index;
    int min;
    int max;

};


void creat_segment_tree(struct segment_tree **root,int *a,int low,int high){
    if(low==high){
        (*root)->max=a[low];
        (*root)->min=a[low];
        (*root)->index=low;
         (*root)->max_index=low;
       
        (*root)->left=NULL;
        (*root)->right=NULL;
       


    }
    else if(high>low){
         int max=a[low];
     
        int min=a[low];
        for(int i=low+1;i<=high;i++){
            if(a[i]>max){
                max=a[i];
               
            }
            if(a[i]<min){
                min=a[i];
            }
        }
        int mid=(low+high)/2;
        (*root)->max=max;
        (*root)->min=min;
        (*root)->index=low;
        (*root)->max_index=high;
        (*root)->left=(struct segment_tree *)malloc(sizeof(struct segment_tree ));
        (*root)->right=(struct segment_tree *)malloc(sizeof(struct segment_tree ));
        creat_segment_tree(&((*root)->left),a,low,mid);
        creat_segment_tree(&((*root)->right),a,mid+1,high);

    }
    else{
        (*root)=NULL;
    }
   
    
}

void  pre_dfs(struct segment_tree *root){
    if(root){
        
       // printf("%d  %d|",root->max,root->min);
        pre_dfs(root->left);
        pre_dfs(root->right);

    }
}


void  find(struct segment_tree *root,int *max_t,int index,int val,int k,int *dp){
    if(root){
      //  printf("%d %d->",root->max,root->index);

  
        if(root->left==NULL&&root->right==NULL&&root->index<index&&root->max<val&&val-root->max<=k){

            *max_t=fmax(dp[root->index]+1,*max_t);
        }
        else{
            if(root->index<index&&val-root->max<=k&&root->min<val){
              
           
                     find(root->right,max_t,index,val,k,dp);
                    find(root->left,max_t,index,val,k,dp);

          
                     
                      
            }

        }
      }
    


}

int lengthOfLIS(int* nums, int numsSize, int k){
    
     int max=1;
     int *max_t=(int *)malloc(sizeof(int));
     int *dp=(int *)malloc(sizeof(int)*numsSize);
     int size=1;
     int pre=nums[0];
    
     for(int i=1;i<numsSize;i++){
         if(nums[i]==pre){
             continue;
         }
         else{
             pre=nums[i];
             nums[size++]=nums[i];
         }
     }
    
     numsSize=size;
     
    
     
      struct tree *root=(struct segment_tree *)malloc(sizeof(struct segment_tree ));
     creat_segment_tree(&root,nums,0,numsSize-1);
    pre_dfs(root);
    
   
     


    dp[0]=1;
    
    
     for(int i=1;i<numsSize;i++){
         *max_t=1;
        find(root,max_t,i,nums[i],k,dp);
         dp[i]=*max_t;
     //    printf("dp %d ",dp[i]);
         
       
         max=fmax(dp[i],max);
     }
     return max;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值