c++刷题(6/100)最长上升子序列

题目一:区间子数组个数

给定一个元素都是正整数的数组A ,正整数 L 以及 R (L <= R)。

求连续、非空且其中最大元素满足大于等于L 小于等于R的子数组个数。

例如 :
输入: 
A = [2, 1, 4, 3]
L = 2
R = 3
输出: 3
解释: 满足条件的子数组: [2], [2, 1], [3].

注意:

  • L, R  和 A[i] 都是整数,范围在 [0, 10^9]
  • 数组 A 的长度范围在[1, 50000]

思路:比较简单,维护住子数组中的那个最大值就行了,如果这个最大值超过了上界,那么直接break,因为再扩大子数组也是徒劳

class Solution {
public:
    int numSubarrayBoundedMax(vector<int>& A, int L, int R) {
        int tempMax ;
        int count = 0 ;
        for(int i=0;i<A.size();i++){
            tempMax = -1 ;
            for(int j=i;j<A.size();j++){
                if(A[j]>tempMax){
                    tempMax = A[j] ;
                }
                if(L<=tempMax&&tempMax<=R){
                    count++ ;
                }else{
                    if(tempMax>R)break ;
                }
            }
        }
        return count ;
    }
};

题目二:最长上升子序列

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4 
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4

说明:

  • 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
  • 你算法的时间复杂度应该为 O(n2) 。

进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

思路:经典题目,用动态规划可以到n^2的时间复杂度,但是动态规划我老想不出来,总觉得状态转移方程比较难想,多积累吧

动态规划的做法,dp数组中存,每一个以dp[i]结尾的最长上升子序列,更新的时候:

                if(nums[i]>nums[j]){
                    dp[i] = max(dp[i],dp[j]+1) ;
                }

整个代码:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.size()<=1){
            return nums.size() ;
        }
        int ans = 1;
        vector<int> dp(nums.size(),1) ;
        for(int i=1;i<nums.size();i++){
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]){
                    dp[i] = max(dp[i],dp[j]+1) ;
                }
            }
        }   
        for(int i=0;i<nums.size();i++){
            ans = max(ans,dp[i]) ;
        }
        return ans ;
    } 
};

题目中还说了,有nlogn的算法,网上查了一下,这篇博客说的比较好:https://blog.csdn.net/will130/article/details/50575967

具体思路是,维护一个上升序列,每次有元素进来,要么直接加后面,要么更新前面第一个比它大的数,这里简单搬运一下

class Solution {
public://
    int lengthOfLIS(vector<int>& nums) {
        int n=nums.size();
        if(n <= 1) return n;
        //tail[i]表示长度为i的递增序列末尾的数字
        //tail[]数组性质:tail[0]<tail[1]<...tail[n] !!!
        vector<int> tail(n);//初始化为n个值为0的元素
        //1.len为当前最长的递增序列长度(为方便操作将len减1,从0开始,最后再加上1)
        int len=0;
        tail[0]=nums[0];
        //2.每次读入一个新元素nums[i]
        for(int i=1;i<n;i++)
        {//遍历nums[]中的数
            if(nums[i] < tail[0])
            {//(1)nums[i]比所有递增序列的末尾都小,则长度为1的序列更新为这个更小的末尾。
                tail[0]=nums[i];
            }
            else if(nums[i] > tail[len])
            {//(2)nums[i]比所有序列的末尾都大,则直接将nums[i]加到后面
                tail[++len]=nums[i];
            }
            else
            {//(3)在中间,则更新那个末尾数字刚好大于等于nums[i]的那个序列,nums[i]替换其末尾数字
                tail[biSearch(tail, 0, len, nums[i])]=nums[i];
            }
        }
        return len+1;
    }
    int biSearch(vector<int>& tail, int low, int high, int target)
    {//由于tail数组是有序的,故可二分查找其中元素
        while(low <= high)//不能是low<high
        {//当low=high时还要进行一次循环!!!
         //此时mid=low=high.若tail[mid]<target,则low=mid+1.而不是直接返回low!!!
            int mid = low + (high-low)/2;
            if(tail[mid] == target) return mid;
            else if(tail[mid] > target)
            {
                high=mid-1;
            }
            else
            {
                low=mid+1;
            }
        }
        return low;
    }
};

 题目三:乘积最大子序列

给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。

示例 1:

输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。

示例 2:

输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
思路:这个题确切的来说应该是子数组,跟最长上升子序列就动归而言有点像,由于是子数组不是子序列,所以可以o(n)完成(子序列需要遍历状态i之前的所有状态,而子数组是连续的,子需要记录上一个状态就行)
但是由于是乘法,所以有符号的问题,上一个状态是负的,乘上一个负数反而可能变的很大,所以这道题的关键是维护两个状态,当前最大和当前最小,每次计算都更新这两个状态,不过只有最大值和ans比较
class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int len = nums.size() ;
        if(len==0){
            return 0 ;
        }
        int tempMax = nums[0] ;
        int tempMin = nums[0] ;
        int ans = nums[0] ;
        int lastMax = nums[0];
        for(int i=1;i<len;i++){
            tempMax = max(max(lastMax*nums[i],nums[i]),tempMin*nums[i]) ;
            tempMin = min(min(lastMax*nums[i],nums[i]),tempMin*nums[i]) ;
            lastMax = tempMax ;
            if(tempMax>ans){
                ans = tempMax ;
            }
        }
        return ans ;
    }
};
 
 

猜你喜欢

转载自www.cnblogs.com/maskmtj/p/9258529.html