• 最长递增子序列


    题目一:最长递增子序列

    题目链接:最长递增子序列

    给出长度为 N 的数组,找出这个数组的最长递增子序列。递增子序列是指,子序列的元素是递增的。

    例如:5 1 6 8 2 4 5 10,最长递增子序列是 1 2 4 5 10

    AC 代码

    演示样例

    import java.util.Scanner;
    
    public class Main {
    
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            int n = scanner.nextInt();
            int[] array = new int[n];
            for (int i = 0; i < n; i++)
                array[i] = scanner.nextInt();
    
            int res = getLIS(array, 0, n);
            System.out.println(res);
        }
    
        // 求 array 的最长递增子序列的长度
        public static int getLIS(int[] array, int start, int length) {
            int[] ans = new int[length + 1];
            ans[1] = array[start];
            int maxLen = 1;
            for (int i = start + 1; i < start + length; i++) {
                if (array[i] > ans[maxLen]) {
                    ans[++maxLen] = array[i];
                } else {
                    int pos = binarySearch(ans, 1, maxLen, array[i]);
                    ans[pos] = array[i];
                }
            }
            return maxLen;
        }
    
        // 二分找到 ans 中第一个大于等于 value 的那个位置
        private static int binarySearch(int[] ans, int left, int right, int value) {
            int mid;
            while (left < right) {
                mid = (left + right) >> 1;
                if (ans[mid] >= value)
                    right = mid;
                else
                    left = mid + 1;
            }
            return left;
        }
    }
    

    题目二:最长非递减子序列

    题目链接

    小明有一袋子长方形的积木,如果一个积木 A 的长和宽都不大于另外一个积木 B 的长和宽,则积木 A 可以搭在积木 B 的上面。好奇的小明特别想知道这一袋子积木最多可以搭多少层,你能帮他想想办法吗?

    定义每一个长方形的长 L 和宽 W 都为正整数,并且 1 <= W <= L <= INT_MAX,袋子里面长方形的个数为 N,并且 1 <= N <= 1000000。
    假如袋子里共有 5 个积木分别为 (2, 2),(2, 4),(3, 3),(2, 5),(4, 5),则不难判断这些积木最多可以搭成 4 层,因为 (2, 2) < (2, 4) < (2, 5) < (4, 5)。

    输入描述

    第一行为积木的总个数 N
    之后一共有 N 行,分别对应于每一个积木的宽 W 和长 L

    输出描述

    输出总共可以搭的层数

    样例输入

    5
    2 2
    2 4
    3 3
    2 5
    4 5

    样例输出

    4

    AC 代码

    import java.util.Arrays;
    import java.util.Comparator;
    import java.util.Scanner;
    
    /**
     * 思路:
     *      先对长方形的宽进行一次排序,然后就变成了求长方形的长的最长非递减子序列
     */
    public class Main {
    
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            int n = scanner.nextInt();
            int[][] array = new int[n][2];
            for (int i = 0; i < n; i++) {
                array[i][0] = scanner.nextInt();
                array[i][1] = scanner.nextInt();
            }
    
            Arrays.sort(array, Comparator.comparingInt(o -> o[0]));
            int res = getLIS(array, 0, n);
            System.out.println(res);
        }
    
        // 求 array 的最长非递减子序列的长度
        public static int getLIS(int[] array, int start, int length) {
            int[] ans = new int[length];
            ans[0] = array[start];
            int maxLen = 1;
            for (int i = start + 1; i < start + length; i++) {
                int pos = binarySearch(ans, 0, maxLen, array[i]);
                maxLen = pos == maxLen ? maxLen + 1 : maxLen;
                ans[pos] = array[i];
            }
            return maxLen;
        }
    
        /**
         * 二分找到 ans 中第一个大于 value 的那个位置
         *
         * 这里 ans 中的数据范围为 [left, right)
         */
        private static int binarySearch(int[] ans, int left, int right, int value) {
            int mid;
            while (left < right) {
                mid = (left + right) >> 1;
                if (ans[mid] > value)
                    right = mid;
                else
                    left = mid + 1;
            }
            return left;
        }
    }
    

    题目三:最长公共子序列

    题目链接:最长公共子序列

    给出 1 - n 的两个排列 P1 和 P2,求它们的最长公共子序列。

    输入描述

    第一行是一个数 n
    接下来两行,每行为 n 个数,为自然数 1 - n 的一个排列。

    输出描述

    一个数,代表最长公共子序列的长度

    样例输入

    5
    3 2 1 4 5
    1 2 3 4 5

    样例输出

    3

    AC 代码

    import java.util.Scanner;
    
    /**
     * 求两个序列的最长公共子序列是典型的动态规划题目,时间复杂度为 O(n^2)
     *
     * 但是这道题中的两个序列满足特定的条件:A B 分别是 1 - n 的两个不同的排列
     *
     * 这时可以将其转化为求一个序列的最长递增子序列,而求一个序列的最长递增子序列的时间
     * 复杂度可以优化为 O(nlogn)
     *
     * 具体实现为:
     *          因为序列 A B 是 1 - n 的两个不同的排列,那么 A 中出现的数字在 B 中
     *          也一定存在。
     *          我们可以用一个数组 pos 记录下 A 中的某个数字在序列 B 中出现的位置,那么
     *          只要求得数组 pos 的最长递增子序列的长度,也就求得了 A 和 B 的最长公共子
     *          序列的长度。
     *
     *      例如:
     *          A[]  1  5  4  2  3
     *
     *       posA[]  1  2  3  4  5
     *
     *        pos[]  5  1  2  4  3        最长递增子序列的长度为 3
     *
     *          B[]  5  4  3  2  1
     *
     *      为什么会这样呢?
     *      实际上我们找的并不是一个具体的数字序列,而是逻辑上的数字出现的顺序序列
     *
     *      如上所示,posA 代表 A 中的数字在 A 中出现的顺序,pos 代表 A 中的数字在 B 中
     *      出现的顺序。
     *      显然,只要找到 pos 中的一个递增子序列,那么这段递增子序列所对应 B 中的一个子序
     *      列一定存在与 A 中,即找到了 A 与 B 的公共子序列。
     *
     *      那么只要找到了 pos 中的最长递增子序列的长度,也就找到了 A B 的最长公共子序列的长度
     *
     */
    public class Main {
    
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            int n = scanner.nextInt();
            int[] A = new int[n];
            for (int i = 0; i < n; i++)
                A[i] = scanner.nextInt();
    
            int[] map = new int[n + 1];
            for (int i = 0; i < n; i++) {
                int tem = scanner.nextInt();
                map[tem] = i + 1;
            }
    
            int[] pos = new int[n];
            for (int i = 0; i < n; i++)
                pos[i] = map[A[i]];
    
            int res = getLIS(pos, 0, n);
            System.out.println(res);
        }
    
        // 求 array 的最长递增子序列的长度
        public static int getLIS(int[] array, int start, int length) {
            int[] ans = new int[length + 1];
            ans[1] = array[start];
            int maxLen = 1;
            for (int i = start + 1; i < start + length; i++) {
                if (array[i] > ans[maxLen]) {
                    ans[++maxLen] = array[i];
                } else {
                    int pos = binarySearch(ans, 1, maxLen, array[i]);
                    ans[pos] = array[i];
                }
            }
            return maxLen;
        }
    
        // 二分找到 ans 中第一个大于等于 array[index] 的那个位置
        private static int binarySearch(int[] ans, int left, int right, int value) {
            int mid;
            while (left < right) {
                mid = (left + right) >> 1;
                if (ans[mid] >= value)
                    right = mid;
                else
                    left = mid + 1;
            }
            return left;
        }
    }
    

    题目四:Array

    题目连接:Array

    小红有两个长度为 n 的排列 A 和 B。每个排列由 [1, n] 数组成,且里面的数字都是不同的。

    现在要找到一个新的序列 C,要求这个新序列中任意两个位置 (i, j) 满足:

    如果在 A 数组中 C[i] 这个数在 C[j] 的后面,那么在 B 数组中需要 C[i] 这个数在 C[j] 的前面。

    请问 C 序列的长度最长为多少呢?

    输入描述

    第一行一个整数,表示 N。
    第二行 N 个整数,表示 A 序列。
    第三行 N 个整数,表示 B 序列。
    满足: N <= 50000

    输出描述

    输出最大的长度

    样例输入

    5
    1 2 4 3 5
    5 2 3 4 1

    样例输出

    4

    说明

    最长的 C 为 [1, 3, 4, 5]

    AC 代码

    import java.util.Scanner;
    
    /**
     * 解题思路:
     *          可以先将数组 B 倒叙为 C,这样题目就转换为了求 A 和 C 的最长公共子序列的长度
     *          但如果用动态规划的话时间复杂度为 O(n^2),题目中的 n <= 50000
     *          显然会超时。
     *
     *          那么就可以直接使用题目二所用的方法,求得 A C 的最长公共子序列的长度
     *          但是这里并没有直接将 B 倒叙,而是在对 pos 赋值的时候对 map 取负,这样就达
     *          到了同样的效果。
     *
     */
    public class Main {
    
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            int n = scanner.nextInt();
            int[] A = new int[n];
            for (int i = 0; i < n; i++)
                A[i] = scanner.nextInt();
    
            int[] map = new int[n + 1];
            for (int i = 0; i < n; i++) {
                int tem = scanner.nextInt();
                map[tem] = i + 1;
            }
    
            int[] pos = new int[n];
            // 对 map 取负值,就行当与求数组 pos 的最长上升子序列
            for (int i = 0; i < n; ++i)
                pos[i] = -map[A[i]];
    
            // 接下来相当于是找 pos 的最长上升子序列,时间复杂度 O(nlogn)
            int res = getLIS(pos, 0, n);
            System.out.println(res);
        }
    
        // 求 array 的最长递增子序列的长度
        public static int getLIS(int[] array, int start, int length) {
            int[] ans = new int[length + 1];
            ans[1] = array[start];
            int maxLen = 1;
            for (int i = start + 1; i < start + length; i++) {
                if (array[i] > ans[maxLen]) {
                    ans[++maxLen] = array[i];
                } else {
                    int pos = binarySearch(ans, 1, maxLen, array[i]);
                    ans[pos] = array[i];
                }
            }
            return maxLen;
        }
    
        // 二分找到 ans 中第一个大于等于 array[index] 的那个位置
        private static int binarySearch(int[] ans, int left, int right, int value) {
            int mid;
            while (left < right) {
                mid = (left + right) >> 1;
                if (ans[mid] >= value)
                    right = mid;
                else
                    left = mid + 1;
            }
            return left;
        }
    }
    
  • 相关阅读:
    jQuery对DOM节点进行操作(插入节点之在元素内部插入)
    jQuery对DOM节点进行操作(创建节点)
    jQuery对元素值操作
    jQuery对元素内容操作-->对HTML内容的操作
    jQuery对元素内容操作-->对文本内容的操作
    jQuery对元素内容操作
    jQuery选择器-->注意事项
    jQuery选择器-->表单选择器
    cp 快捷命令:复制文件到多个目录
    Python-字符串
  • 原文地址:https://www.cnblogs.com/debugxw/p/11335464.html
Copyright © 2020-2023  润新知