【算法】子序列问题合集

子序列 最大值有三种情况: 1、所有正数加起来是奇数 2、所有正数加起来是偶数,那么就比较减去最小奇正数和加上最大奇负数哪个大 3、如果没有正数那么答案就是最大奇负数 其中第一种情况和第三种情况是很好解决的,难就难在第二种情况,我昨天编写了两个多小时,思路早就想好了,只差一步了,然后又遇到了在AcWing上面的输出结果和我在C++编译器上面的输出结果不一样(程序一模一样,输入一模一样)的问题,简直烦死我了。 阅读详情

前言

动态规划的核心设计思想是数学归纳法

假如我们想证明一个数学结论:

  1. 那么先假设这个结论在 k < n 时成立
  2. 想办法推导证明出 k = n 的时候此结论也成立。

是需要一个 dp 数组嘛?

  1. 可以假设 dp[0...i - 1] 都已经被算出来了
  2. 然后问自己:怎么通过这些结果算出 dp[i]?
  3. 首先要定义清楚的 dp 数组的含义,即 dp[i] 的值到底代表是什么?

Tips:”子序列“ 和 ”子串“ 区别

  • 子串一定是连续的
  • 子序列不一定是连续的

子序列问题是最常见的算法问题,而且并不好解决。

一旦涉及子序列和最值,那几乎可以肯定,考察的是动态规划技巧,时间复杂度一般都是 O(n ^ 2)

既然用到动态规划,那就要定义 dp 数组,寻找状态转移关系。

两种思路:

  1. 第一种思路模板是一个一维的 dp 数组:
int n = array.length;
int [] dp = new int[n];

for (int i = 1; i < n; ++i) {
    for (int j = 0; j < i; ++j) {
        dp[i] = 最值(dp[i], dp[j] + ...)
    }
}

  1. 第二种思路模板是一个二维的 dp 数组:
int n = arr.length;
int [][] dp = new int[n][n];

for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
        if (arr[i] == arr[j]) 
            dp[i][j] = dp[i][j] + ...
        else
            dp[i][j] = 最值(...)
    }
}

题目

(1)LeetCode 300 最长递增子序列

题目描述


给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:
输入:nums = [0,1,0,3,2,3]
输出:4

示例 3:
输入:nums = [7,7,7,7,7,7,7]
输出:1

思路解法


定义是这样的:dp[i] 表示以 nums[i] 这个数结尾的最长递增子序列的长度。

可以推出 base case:dp[i] 初始值为 1, 因为以 nums[i] 结尾的最长递增子序列起码要包含它自己。

public class LeetCode_300 {

    // 方法一:
    // Time: o(n^2), Space: o(n), Faster: 27.93%
    public int lengthOfLISDP(int [] nums) {
        if (nums == null || nums.length == 0) return 0;
        int n = nums.length, max = 1;
        int [] d = new int[n];
        d[0] = 1;

        for (int i = 1; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                int cur = nums[i] > nums[j] ? d[j] + 1 : 1;
                d[i] = Math.max(d[i], cur);
            }
            max = Math.max(max, d[i]);
        }
        return max;
    }

    // 方法二:
    // Time: o(n * log(n)), Space: o(n), Faster:  92.62%
    public int lengthOfLISBinarySearch(int[] nums) {
        if (nums == null || nums.length == 0) return 0;
        // 牌堆数初始化为 0
        int len = 0;
        int [] top = new int[nums.length];
        for (int x : nums) {
            int left = binarySearchInsertPosition(top, len, x);
            // 把这张牌放到牌堆顶,牌堆顶的牌是这个堆最小的
            top[left] = x;
            // 没找到合适的牌堆,新建一堆
            if (left == len) ++len;
        }
        return len;
    }

    private int binarySearchInsertPosition(int[] d, int len, int x) {
        int low = 0, high = len - 1;
        while (low <= high) {
            int mid = low + (high - low) / 2;
            if (x < d[mid]) high = mid - 1;
            else if (x > d[mid]) low = mid + 1;
            else return mid;
        }
        return low;
    }

}

(2)LeetCode 53 最大子序和

题目描述


给定一个整数数组 nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

思路解法


题目解读:以 nums[i] 为结尾的 “最大子数组和” 为 dp[i]

使用数学归纳法来找状态转移关系:假设已经算出了 dp[i - 1], 如何推导出 dp[i] 呢?

dp[i] 有两种 “选择”:

  1. 要么与前面的相邻子数组连接,形成一个和更大的子树组
  2. 要么不与前面的子数组连接,自成一派,自己作为一个子数组。
// 要么自成一派,要么和前面的子数组合并
dp[i] = Math.max(nums[i], num[i] + dp[i - 1]);

思路:

  1. 方法一:暴力求法,两层 for 循环
  2. 方法二:dp方法。
  3. 方法三:遍历一遍,负数 + 负数没有任何意义 前面累加值为负数,则对后面没有正向的贡献, 所以可以直接舍弃前面这段子序列的和

这里主要讲的是方法三:

public class LeetCode_53 {

    // Time: O(n), Space: O(1), Faster: 95.05%
    public int maxSumOfSubArray(int[] nums) {
        int max = Integer.MIN_VALUE, cur = 0;

        for (int i = 0; i < nums.length; ++i) {

            // 前面累加值为负数,对后面没有正向的贡献,可以直接舍弃前面的子序列的和
            cur = cur <= 0 ? nums[i] : (cur + nums[i]);
            max = Math.max(max, cur);
        }

        return max;
    }
}

总结:

  • dp 大部分还是有规律可循的。

  • dp 数组的定义是 “以 nums[i] 为结尾的最大子数组和 / 最长递增子序列为 dp[i] ”。

  • 因为只有这样的定义才能使 dp[i + 1] 和 dp[i] 建立联系,利用数学归纳发写出状态转移方程。

(3)LeetCode 152 连续子序列的最大乘积

题目描述


这个题目说的是,给你一个整数数组,你要找到乘积最大的连续子序列,然后返回它的乘积。

比如说,给你的数组是:

8, 1, -2, 4, -1

这个数组中连续子序列的最大乘积是 64,连续子序列就是数组本身。

思路解法


解法有两种:

  1. 暴力法:两层 for 循环
  2. 动态规划:在遍历数组时候,要保留当前最大乘积、当前最小乘积
    • 当前最大乘积
    • 当前最小乘积

图解如下:

AC 代码:

public class LeetCode_152 {

    // 方式一:暴力法
    // Time: O(n ^ 2), Space: O(1), Faster: 5.01%
    public int maxProduct2(int[] nums) {
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < nums.length; ++i) {
            int sum = nums[i];
            max = Math.max(max, sum);
            for (int j = i + 1; j < nums.length; ++j) {
                sum *= nums[j];
                max = Math.max(sum, max);
            }
        }
        return max;
    }

    // 方式二:动态规划
    // Time: o(n), Space: o(1), Faster: 66.15%
    public int maxProduct(int[] nums) {
        if (nums == null || nums.length == 0) return 0;
        int max = nums[0], currMax = nums[0], currMin = nums[0];

        for (int i = 1; i < nums.length; ++i) {
            int a = currMax * nums[i], b = currMin * nums[i], c = nums[i];
            currMax = max(a, b, c);
            currMin = min(a, b, c);
            max = Math.max(max, currMax);
        }

        return max;
    }

    private int max(int a, int b, int c) {
        return Math.max(Math.max(a, b), c);
    }

    private int min(int a, int b, int c) {
        return Math.min(Math.min(a, b), c);
    }
}

(4)LeetCode 354 俄罗斯套娃信封问题

题目描述


给定一些标记了宽度和高度的信封,宽度和高度以整数对形式 (w, h) 出现。当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。

请计算最多能有多少个信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。

说明: 不允许旋转信封。

示例:

输入: envelopes = [[5,4],[6,4],[6,7],[2,3]]
输出: 3 
解释: 最多信封的个数为 3, 组合为: [2,3] => [5,4] => [6,7]。

信封嵌套问题实际上是最长递增子序列问题上升到二维,其解法就需要先按照特定的规则排序,之后转换为一个一维的最长递增子序列问题,最后用二分搜索框架中的技巧来解决。

这道题目其实是 最长递增子序列(LOngest Incresing Subsequence, LIS) 的一个变种。

每次合法的嵌套是大的套小的,相当于找一个最长递增的子序列,其长度就是最多能嵌套的信封个数。

一个直接的想法就是,通过 w * h 计算面积,然后对面积进行标准的 LIS 算法。但是稍加思考就会发现这样不行,比如 1 * 10 大于 3 * 3, 但是显然这样的两个信封是无法互相嵌套的。

思路解法


思路步骤:

  1. 先对宽度 w 进行升序排序,如果遇到 w 相同的情况,则按照高度 h 降序排序。
  2. 之后把所有的 h 作为一个数组,在这个数组上计算出的 LIS 的长度就是答案。

步骤1,如图:

步骤2,如图:

public class LeetCode_354 {

    // Time: O(n * log(n)), Space: O(n), Faster: 87.03%
    public int maxEnvelopes(int[][] envelopes) {

        int n = envelopes.length;
        // 按照宽度升序排列,如果宽度一样,则按高度降序排列
        Arrays.sort(envelopes, (a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]);

        // 对高度数组寻找 LIS
        int [] height = new int[n];
        for (int i = 0; i < n; ++i) {
            height[i] = envelopes[i][1];
        }

        return lengthOfLISBinarySearch(height);
    }

    // Time: o(n * log(n)), Space: o(n), Faster:  92.62%
    private int lengthOfLISBinarySearch(int[] nums) {
        if (nums == null || nums.length == 0) return 0;
        // 牌堆数初始化为 0
        int len = 0;
        int [] top = new int[nums.length];
        for (int x : nums) {
            int left = binarySearchInsertPosition(top, len, x);
            // 把这张牌放到牌堆顶
            top[left] = x;
            // 没找到合适的牌堆,新建一堆
            if (left == len) ++len;
        }
        return len;
    }

    private int binarySearchInsertPosition(int[] d, int len, int x) {
        int low = 0, high = len - 1;
        while (low <= high) {
            int mid = low + (high - low) / 2;
            if (x < d[mid]) high = mid - 1;
            else if (x > d[mid]) low = mid + 1;
            else return mid;
        }
        return low;
    }
}

总结:

其实这种问题还可以扩展到三维,比如过说嵌套箱子,每个箱子有长、宽、高三个维度,请你计算最多能嵌套几个箱子?

按之前思路,可能会想:先把前两个维度(长和宽)按信封嵌套的思路求一个嵌套序列,最后在这个序列的第三个维度(高度)找一下 LIS,就能算出答案。

实际上,这个思路是错误的。这类问题叫做 “偏序问题”,上升到三维会使难度巨幅提升,需要借助一种高级数据结构 “树状数组”。

(5)LeetCode 1312 让字符串成为回文串的最少插入次数

题目描述


给你一个字符串 s ,每一次操作你都可以在字符串的任意位置插入任意字符。

请你返回让 s 成为回文串的 最少操作次数 。

「回文串」是正读和反读都相同的字符串。

示例 1:

输入:s = "zzazz"
输出:0
解释:字符串 "zzazz" 已经是回文串了,所以不需要做任何插入操作。
示例 2:

输入:s = "mbadm"
输出:2
解释:字符串可变为 "mbdadbm" 或者 "mdbabdm" 。
示例 3:

输入:s = "leetcode"
输出:5
解释:插入 5 个字符后字符串变为 "leetcodocteel" 。
示例 4:

输入:s = "g"
输出:0
示例 5:

输入:s = "no"
输出:1

提示:

1 <= s.length <= 500 s 中所有字符都是小写字母。

题干分析


要找最少的插入次数,那肯定要穷举喽。

每次都可以在两个字符的中间插入任意一个字符,外加判断字符串是否为回文字符串,它的时间复杂度肯定暴增,而且是指数级的。

动态规划标准套路:

  1. 第一步要明确:状态 和 选择
  2. 第二步要明确:dp 数组的定义
  3. 第三步:根据 “选择”,思考状态转移的逻辑

1. 第一步要明确:状态 和 选择

  1. 状态:“指针 i” 和 “指针 j”
  2. 选择:“插入” 或者 “不插入”

2. 第二步要明确:dp 数组的定义

  1. dp数组的定义:dp[i][j] , 对字符串 s[i..j],最少需要进行 dp[i][j] 次插入才能变成回文串。
  1. 最终答案:dp[0][n - 1] 的大小(n 为 s 的长度)
  1. base case: 当 i == j 时,dp[i][j] = 0, 因为当 i == j 时 s[i..j] 就是一个字符,本身就是回文串,所以不需要进行任何插入操作;

3. 第三步:根据 “选择”,思考状态转移的逻辑

  1. 如果 s[i] == s[j],不需要进行任何插入,只要知道如何把 s[i+1..j-1] 变成回文串即可
  1. 如果 s[i] != s[j],分情况讨论,看下文。

分析第三步:状态转移方程

想要计算 dp[i][j] 的值,能从子问题 dp[i + 1][j - 1] 推出 dp[i][j] 值嘛?

也就认为 s[i+1 .. j-1] 已经是一个回文串了,所以通过 dp[i+1][j-1] 推导 dp[i][j] 的关键在于 s[i] 和 s[j]。

  1. 如果 s[i] == s[j],不需要进行任何插入,只要知道如何把 s[i+1..j-1] 变成回文串即可。

如图:

// 翻译成代码
if (s[i] == s[j]) {
    dp[i][j] = dp[i + 1][j - 1];
}

  1. 如果 s[i] != s[j],分情况讨论:

当 s[i] != s[j] 时,插入两次肯定可以让 s[i..j] 变成回文串,但是不一定是插入次数最少的。

如图:

第一步:将 s[i] 插到 s[j] 的左边

第二步:将 s[j] 插到 s[i] 的左边

特殊情况:

只需要插入一个字符即可使得 s[i..j] 变成回文,如下图:

这种情况,s[i+1..j] 或 s[i..j - 1] 原本就是回文串,那么就不需要插入,只需要插入另一个即可。

if (s[i] != s[j]) {
    // 步骤一选择代价较小的
    // 步骤二必然要进行一次插入
    dp[i][j] = Math.min(dp[i + 1][j], dp[i][j - 1]) + 1;
}

思路解法


状态转移方程中 dp[i][j] 和 dp[i + 1][j] 、dp[i][j - 1]、dp[i + 1][j - 1] 三个状态有关,为了保证每次计算 dp[i][j] 时,这三个状态都已经被计算,一般选择从下向上,从左到右遍历 dp 数组:

public class LeetCode_1312 {

    // Time: O(n ^ 2), Space: O(n ^ 2), Faster: 60.65%
    public int minInsertions(String s) {
        int n = s.length();

        // 定义: 对 s[i..j], 最少需要插入 dp[i][j] 次才能变成回文串
        // base case: dp 数组全部初始化为 0
        int [][] dp = new int[n][n];

        // 从下向上遍历
        for (int i = n - 2; i >= 0; --i) {
            // 从左向右遍历
            for (int j = i + 1; j < n; ++j) {
                // 根据 s[i] 和 s[j] 进行状态转移
                if (s.charAt(i) == s.charAt(j)) {
                    dp[i][j] = dp[i + 1][j -1];
                } else {
                    dp[i][j] = Math.min(dp[i + 1][j], dp[i][j - 1]) + 1;
                }
            }
        }
        // 根据 dp 数组的定义,题目要求的答案是 dp[0][n - 1]
        return dp[0][n - 1];
    }
}

优化:

dp 数组的状态只和和它相邻的状态有关,所以 dp 数组是可以压缩成一维的。

public class LeetCode_1312 {

    // Time: O(n ^ 2), Space: O(n), Faster: 77.81%
    public int minInsertions(String s) {
        int n = s.length();

        int [] dp = new int[n];

        int temp = 0;
        for (int i = n - 2; i >= 0; --i) {
            // 记录 dp[i+1][j-1]
            int pre = 0;
            for (int j = i + 1; j < n; ++j) {
                temp = dp[j];

                if (s.charAt(i) == s.charAt(j)) {
                    // dp[i][j] = dp[i+1][j-1];
                    dp[j] = pre;
                } else {
                    // dp[i][j] = Math.min(dp[i + 1][j], dp[i][j - 1]) + 1;
                    dp[j] = Math.min(dp[j], dp[j - 1]) + 1;
                }
                pre = temp;
            }
        }

        return dp[n - 1];
    }
}

(6)LeetCode 1143 最长公共子序列

题目描述


给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。

若这两个字符串没有公共子序列,则返回 0。

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3
解释:最长公共子序列是 "ace",它的长度为 3。

示例 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc",它的长度为 3。

示例 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0。

题干分析


最长公共子序列(Longest Common Subsequence, LCS)是一道非常经典的面试题目,因为它的解法是典型的二维动态规划。

这个算法稍微加改造就可以用于解决其他问题,所以说 LCS 算法是值得掌握的。

动态规划思路:

  1. 第一步:一定要明确 dp 数组的含义
  2. 定义 base case
  3. 找状态转移方程

1. 第一步:一定要明确 dp 数组的含义。

对于两个字符串的动态规划问题,套路是通用的,一般都需要一个二维 dp 数组。

比如对于字符串 s1 和 s2,一般来说要构造一个这样的 DP table:

dp[i][j] 含义是:对于 s1[0..i - 1] 和 s2[0..j - 1],它们的 LCS 长度是 dp[i][j]。

比如上图这个例子,dp[2][4] = 2 含义就是:对于 "ac" 和 "babc",它们的 LCS 长度是2。根据这个定义,最终想得到的答案应该是 dp[3][6]。

2. 定义 base case

专门让索引为 0 的行和列表示空串,dp[0][..] 和 dp[..][0] 都应该初始化为 0,这就是 base case。

比如按照刚才 dp 数组的定义,dp[0][3] = 0 的含义是:对于空字符串 "" 和 "bab",其 LCS 的长度为 0。

因为有一个字符串是空串,它们的最长公共子序列的长度显然应该是 0

3. 找状态转移方程

做 “选择”。

求 s1 和 s2 的最长公共子序列,设:子序列为 lcs。

那么对于 s1 和 s2 中的每个字符,有什么选择?

很简单,两种选择,要么在 lcs 中,要么不在。

如图:

对于 s1[i] 和 s2[j],怎么知道它们到底在不在 lcs 中?

可以很容易想到的是,如果某个字符在 lcs 中,那么这个字符肯定同时存在与 s1 和 s2 中,因为 lcs 是最长公共子序列。

dp(i, j) 表示 s1[0...i] 和 s2[0...j] 中最长公共子序列的长度,这样就可以找到状态转移关系:

  1. 如果 s1[i] == s2[j]

说明这个公共字符一定在 lcs 中,如果知道了 s1[0..i-1] 和 s2[0..j-1] 中的 lcs 长度,再加 1 就是 s1[0..i] 和 s2[0..j] 中 lcs 的长度。 根据 dp 函数的定义,如下:

if (str1[i] == str2[j]) dp[i][j] = dp(i - 1, j - 1) + 1;
复制代码
  1. 如果 s1[i] != s2[j]

说明 s1[i] 和 s2[j] 这两个字符至少有一个不在 lcs 中,那到底是哪个字符不在 lcs 中呢? 根据 dp 函数的定义,如下:

if (str1[i] != str2[j]) dp[i][j] = Math.max(dp(i - 1, j), dp(i, j - 1));

明白了状态转移方程,可以直接写出解法。

思路解法


思路:

  1. 二维数组 dp
  2. dp 压缩
public class LeetCode_1143 {
    // Time: O(n ^ 2), Space: O(m * n), Faster: 72.28%
    public int longestCommonSubsequence(String text1, String text2) {

        int m = text1.length(), n = text2.length();
        // 定义: 对于 s1[0..i-1] 和 s2[0..j-1], 它们的 lcs 长度是 dp[i][j]
        // base case: dp[0][..] = dp[..][0] = 0 已初始化
        int [][] dp = new int[m + 1][n + 1];

        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                // 状态转移逻辑
                if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
                }
            }
        }
        return dp[m][n];
    }
}
【排序25:俄罗斯套娃信封问题】(动态规划 / 二维最长公共子序列 / 基于二分查找的动态规划) 给你一个二维整数数组 envelopes ,其中 envelopes[i] = [wi, hi] ,表示第 i 个信封的宽度和高度。当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。请计算 最多能有多少个 信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。注意:不允许旋转信封。示例 1:输入:envelopes = [[5,4],[6,4],[6,7],[2,3]]输出:3。... 阅读详情

相关推荐

最长上升子序列题目大合集

先看看第一题,再由点到面地延伸: 1759:最长上升子序列 描述 一个数的序列bi,当b1 b2 bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, ..., aN),我们可以得到一些上升的子序列(ai1, ai2, ..., aiK),这里1 i1 i2 iK  你的任务,就是对于给定的序列,求出最长上升子序列的长度。 输入

Good good study Day day up~\(≧▽≦)/~ 4108

动态规划算法(子序列专题1)

子数列,又称子序列,在数学中,某个序列的子序列是从最初序列通过去除某些元素但不破坏余下元素的相对位置(在前或在后)而形成的新序列。例如,[3,6,2,7]是数组的子序列。下面我们将通过几道经典例题来进行讲述根据经验+题目要求,我们可以得到如下定义:dp[i]表示,以下标i元素为结尾的递增子序列的最长长度根据题目+经验,dp[i]表示:以i为结尾的斐波那契式的子序列的最长的长度状态分析:新的状态定义:dp[i] [j]表示:以i及j位置为结尾的斐波那契式子序列的最长的长度(i<j)

m0_52882232的博客 2234

模板 - 最长上升子序列与最长公共子序列

整理的算法模板合集: ACM模板 目录1.最长上升子序列(LIS)1.1树状数组优化O(nlogn)O(nlogn)O(nlogn)2.最长公共子序列(LCS)2.1转换成LIS优化O(nlogn)O(nlogn)O(nlogn) 1.最长上升子序列(LIS) 给出一个⻓度为n的数字序列ai{ai}ai,我们需要找到一个最⻓的子序列, 使得子序列中的数字大小单调递增。 例如:n=5n = 5n=5,原序列为(1,4,2,3,5)(1,4,2,3,5)(1,4,2,3,5),那么我们选择(1,2,3,5)(

繁凡さん的博客 3559

数据结构与算法 -- 子序列问题

一点一滴学习算法,算法从入门到放弃。

u012967763的专栏 2514

子序列、子串问题合集-动态规划算法的运用

最大连续子序列和、最长不下降子序列、最长公共子序列、最长回文子串

编程小鹏的博客 228

【自动驾驶端到端5:VADv2: End-to-End Vectorized Autonomous Driving via Probabilistic Planning】

从大规模人类驾驶演示中学习类人驾驶策略是有希望的。但是,规划的不确定性使得从驾驶演示中提取驾驶知识是有挑战性的。从统计学习的角度来看,动作(包括实际和速度)是高度随机的,受许多无法建模的潜在因素影响。

weixin_40000560的博客 1405

“子序列问题”系列总结,一文读懂(Java实现)

该如何使用动态规划去解决有关“子序列”这一系列问题?

CYK_byte的博客 3410

若依前后端分离版集成nacos

服务动态配置与服务注册发现根据公司要求,需要将项目集成到nacos中,当前项目是基于若依前后端分离版开发的,若依的版本为3.8.3,若依框架中整合的springBoot版本为2.5.14。Nacos核心提供两个功能:服务注册与发现,动态配置管理。

NDF923的专栏 4645

算法分析与设计CH15:动态规划算法详解合集——装配线问题、矩阵链乘、LCS(最长公共子序列问题)、最大子数组和问题

动态规划算法合集,四大经典问题求解(背包问题的动态规划求解见贪心章节)

weixin_45745854的博客 839

雷赛控制卡全方位使用指南:从硬件连接到高级应用

雷赛控制卡以其接口简单、编程便捷、性价比高的特点,成为中小自动化设备的理想选择。使用时需注意:硬件连接需注重抗干扰设计(屏蔽线、接地);软件开发需合理配置运动参数(脉冲当量、加减速);调试阶段需充分利用 DMCStudio 工具验证性能。通过本文介绍的方法,可快速实现从硬件搭建到复杂运动控制的全流程开发,满足雕刻、切割、装配等多场景的自动化需求。

品质生活,极致编程。 3773

三维重建Fusion中的TSDF算法通俗描述

看个视频 https://www.youtube.com/watch?v=8M_-lSYqACo 这是mobilefusion的视频,大家先了解一下,看到在三维重建的时候外面有个长方体包围盒了吗? 数据准备: 我们不同角度拍摄的RGB图、深度图和相应的pose.txt。 步骤: 1.建立长方体包围盒 要建立一个长方体包围盒。让所有的三维点都在这个长方体里面。 假设z方向垂直相机。 很明显,x,y方...

qq_30339595的博客 1万+

java常见算法合集

java常见算法合集

weixin_45226668的博客 1374

PD详解—第一章—Type-C连接

Type-C连接 今天大师匈下班比较早,再给大家侃一侃PD,希望大家支持。 PD和Type-c的关系 首先讲一下PD快充的实现步骤,本文主要是Source端(电源),Sink端的差不多也一样,只是策略不同而已。 TypeC连接-->广播电源能力Source capbility(数据交互)->打电压电流->等待其他指令。 所以typec连接是第一步,很多人会从字面意思理解typec连接,这不就是把线给插上去就好了吗? 不知道有没有优秀的同学想到正确答案; 正确答案:typec连接的本质是为

大师匈的博客 1万+

最*子序列,子串问题合集(转)

from:https://blog.youkuaiyun.com/wangdd_199326/article/details/76464333 花了一天时间把一直以来的“最大子序列”、“最大递增子序列“、”最大公共子序列“、“最长公共子串”等问题总结了一下。其中参考了若干博文,都备注引用。 首先子序列是指一个一个序列中,由若个数(字母)组成,然后从中任意删掉几个数(字母),保留剩下的数(字母)构成了一个序列,即称子序列。(或者从一个序列中,任意选取若干数或字符,按照这些数或字符原来的排序组成新的序列,即称子序列).

maxzcl的博客 427

第二章:创建交互式地图

那么您应该已经有了一个安装了这些包的 conda 环境。否则,您需要创建一个新的 conda 环境并使用以下命令安装。

韩天放 4217

Open3D 机载点云电力线提取【2025最新版】

传统方法实现的机载点云电力线分割提取。博客长期更新,本文最新更新时间为:2025年3月23日。

点云侠的博客 4468

C++二分算法:1713得到子序列的最少操作次数

给你一个数组 target ,包含若干 互不相同 的整数,以及另一个整数数组 arr ,arr 可能 包含重复元素。 每一次操作中,你可以在 arr 的任意位置插入任一整数。比方说,如果 arr = [1,4,1,2] ,那么你可以在中间添加 3 得到 [1,4,3,1,2] 。你可以在数组最开始或最后面添加整数。 请你返回 最少 操作次数,使得 target 成为 arr 的一个子序列。 一个数组的 子序列 指的是删除原数组的某些元素(可能一个元素都不删除),同时不改变其余元素的相对顺序得到的数组。比方说

闻缺陷则喜何志丹 3058

从零训练Qwen3-VL

如果你未来想做的是「在公开权重上继续做大规模多模态 pretrain(比如换数据、换目标函数)」而不是在完全随机初始化上起步,这两类代码会比 Open-Qwen2VL 更直接。参考 Qwen2-VL / Qwen2.5-VL 披露的流程,可以大致推测 Qwen3-VL 也是类似的三阶段:(直接在学术算力(8×A100-40G)上从零预训练了一个 2B 参数的多模态 Qwen2-VL 风格模型:(的配置和训练脚本,理解它是如何把「任意 ViT + 任意 LLM + 任意图文数据」拼在一起的。

u013250861的博客 564

C++二分查找算法的应用:300最长递增子序列

二分查找 单调映射。

闻缺陷则喜何志丹 2195

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

最长递增子序列问题合集

最长递增子序列问题 合并LIS(JLIS) 第k个最大递增子序列(KLIS)

qq_39838607的博客 262

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

算法Day4:分治法——快排、归并排序、最大子序列问题

分治法(divide&conquer) 思想: 分而治之 步骤: 先找重复性{最近重复性,最优重复性(动态规划)} 分解成子问题(结构一样规模不同所以适用递归) 最后组合每个子问题的结果 (1)将原问题分解成若干个较小的子问题(子问题与原问题结构一样,只是规模不一样) (2)子问题又不断分解成规模更小的子问题,直到不能再分解或可以轻易计算出子问题的解 (3)利用子问题的解推导出原问题的解...

JohnnyLin 1114

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

算法合集

一、动态规划 1、最长回文字符串 Longest Symmetric String int len = s.length(), ans = 1; for(int i = 0; i < len; i++) { dp[i][i] = 1; if(i < len - 1 && s[i] == s[i+1]) { dp[i][i+1] =...

485

【Android开发】算法题合集(四)找不同和判断子序列

Android面试题算法篇,由本人整理汇总,后续将继续推出系列篇,如果喜欢请持续关注和推荐,更多精彩内容可以扫描文末二维码加v获得更多咨询~上接: 【Android面试】算法题合集(一)二叉树和反转字符串 【Android开发】算法题合集(二)相对名次和反转字符串中的元音字母 【Android开发】算法题合集(三)赎金信和字符串中的第一个唯一字符给定两个字符串 s 和 t,它们只包含小写字母。字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。请找出在 t 中被添加的字母。示例1: 示例2: 示

Eqiqi的博客 357

经典DP问题合集

转发来自 一、上台阶问题 二、矩阵最小路径和 三、最长递增子序列 求一个序列的最长递增子序列,这样的子序列是允许中间越过一些字符的,即留“空”。 例如:4 2 3 1 5 的最长递增子序列为 2 3 5,长度为 3 。 解法:解法 这里给出两种动态规划的做法,第二种是比较优化的 dp 四、最长公共子序列 最长公共链接 回溯 五、背包问题 dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]) ...

qq_32256033的博客 1327
上一篇: 深入了解 JSP 的运行过程
下一篇: 太厉害了!学透这600多页JAVA面试最新宝典,10月让你成功定级P8
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值