【算法题】牛牛的数列-DP问题

牛牛的数列

https://ac.nowcoder.com/acm/problem/13134

链接:https://ac.nowcoder.com/acm/problem/13142
来源:牛客网

题目描述
牛牛现在有一个n个数组成的数列,牛牛现在想取一个连续的子序列,并且这个子序列还必须得满足:最多只改变一个数,就可以使得这个连续的子序列是一个严格上升的子序列,牛牛想知道这个连续子序列最长的长度是多少。
输入描述:

输入包括两行,第一行包括一个整数n(1 ≤ n ≤ 10^5),即数列的长度;
第二行n个整数a_i, 表示数列中的每个数(1 ≤ a_i ≤ 10^9),以空格分割。

输出描述:

输出一个整数,表示最长的长度。

示例1

6 
7 2 3 1 5 6

输出

5

去百度查了下什么叫做“严格上升的子序列”

https://baike.baidu.com/item/%E4%B8%A5%E6%A0%BC%E9%80%92%E5%A2%9E/5318845
递增(increasing)函数是指当函数的任何自变量增加的时候,函数值不减少。严格递增(strongly increasing)是指当函数任何自变量增加的时候函数值也增加。类似地,递减函数(decreasing)是指当函数的任何自变量增加的时候函数值不增加,严格递减(strongly decreasing)是指当函数任何自变量增加的时候函数值却减少。

解题思路:

  1. 本题需要考虑间断路径和不间断路径两种场景。所以定义Dep[n]和DepBreak[n]分别表示不间断路径和间断路径。
  2. 考虑到执行效率问题,需要从右到左完成动态规划,利用上一次的结果计算本次结果。(亲身尝试,暴力破解会超时)

解题步骤:

  1. 对于不间断的情况:在循环遍历第i个数字时,如果第i个数字不大于第i+1个数组(iNums[i]<=iNums[i+1]),则Dep[i]=Dep[i+1]+1,当然,因为这个是不间断的情况,所以DepBreak也要做同样的操作DepBreak[i]=DepBreak[i+1]+1。
  2. 对于间断的情况:在循环遍历第i个数字时,如果跨越1个数字后满足要求间断要求,则允许间断计数,即iNums[i]<=iNums[i+2],跨间断计数。
    间断计数原则:基于无间断的结果Dep[i+2]直接加上2个。
    此时要比较【Dep[i+2]+2】和【按照不间断的方法得到的DepBreak[i]】,大着才是间断情况的最大值。
  3. 需要注意:暴力破解会超时、输入个数小于等于2时需要特殊处理。
import java.util.Arrays;
import java.util.Collections;
import java.util.Scanner;

/**
 * @return maxLenth
 */
public class Main20201121_2 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()){
            // 输入
            Integer n = sc.nextInt();
            Integer[] iNums = new Integer[n];
            Integer[] iDep = new Integer[n];
            Integer[] iDepBreak = new Integer[n];
            for (int i=0; i<n; i++){
                iNums[i] = sc.nextInt();
                iDep[i] = 1;
                iDepBreak[i] = 1;
            }
            // 计算步长
            if (n>2){
                for (int i=n-2; i>=0; i--){
                    // 不间断情况
                    if (iNums[i] <= iNums[i+1]){
                        iDep[i] = iDep[i+1] + 1;
                        iDepBreak[i] = iDepBreak[i+1] + 1;
                    }
                    if (i == n-2 && iNums[i] > iNums[i+1]){
                        iDepBreak[i]++;
                    }
                    // 间断的情况
                    if (i<n-2){
                        if (iNums[i] <= iNums[i+2]){
                            iDepBreak[i] = Math.max(iDep[i+2]+2, iDepBreak[i]);
                        }
                    }
                }
                // 打印结果
                System.out.println(Collections.max(Arrays.asList(iDepBreak)));
            }
            else // n<=2的情况做特殊处理
                // 打印结果
                System.out.println(String.valueOf(n));
        }
    }
}
全部评论
思路: 如果只需要判断一个子序列是否能修改一个数就变成严格上升的子序列,那么可以采用贪心的思想,从左往右遍历一遍数组,维护一个单调递增的栈,如果当前遍历到的数小于栈顶元素,那么就把栈顶元素弹出。最终剩下的元素就组成了一个非严格上升子序列,可以改变其中的一个数变成严格上升子序列。如果剩下的元素个数等于原序列的长度,那么原序列已经是一个升序序列,无需修改。如果剩下的元素个数为1,那么可以把原序列中任意一个位置的数改成剩下的这个数,得到严格上升子序列。如果剩下的元素个数大于1,那么只能改变一个数,使得这些元素可以排成一个严格上升子序列。 但是,题目要求的是最长连续子序列,那么可以将原数组左右各复制一遍,然后在这个新数组中找到最长的满足条件的子序列,并且这个子序列不能跨越原数组的边界。 具体实现时,可以将新数组左右两端的元素置为正无穷大,然后从左往右遍历一遍,维护一个从左端点开始的最长满足条件的连续子序列,用left[i]表示从当前位置开始的最长满足条件的连续子序列的左端点。然后从右往左遍历一遍,维护一个从右端点开始的最长满足条件的连续子序列,用right[i]表示从当前位置开始的最长满足条件的连续子序列的右端点。最终找到一个位置i,使得left[i-1]和right[i+1]之间的元素是最长的满足条件的连续子序列,但是不能跨越原数组的边界,即left[i-1]和right[i+1]必须在原数组范围内。 时间复杂度:O(n) 空间复杂度:O(n)
点赞 回复 分享
发布于 2023-04-13 00:56 广东

相关推荐

牛客771574427号:恭喜你,华杰
点赞 评论 收藏
分享
拉丁是我干掉的:把上海理工大学改成北京理工大学。成功率增加200%
点赞 评论 收藏
分享
3 收藏 评论
分享
牛客网
牛客企业服务