zoukankan      html  css  js  c++  java
  • 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 ;
        }
    };
     
     
  • 相关阅读:
    html——黑体、斜体、下划线及删除线
    <转>DataGridView分页控件
    (转)Log4J 最佳实践之全能配置文件
    C# 单例代码
    MySql基本语句
    .NET中windows服务如何获取自己的安装路径
    web开发网址收藏...
    将字符串转换为json对象_正确语法
    (转)理解矩阵一、二、三
    将数据写入EXCEL多个表
  • 原文地址:https://www.cnblogs.com/maskmtj/p/9258529.html
Copyright © 2011-2022 走看看