动态规划 之 子序列系列 算法专题

一. 最长递增子序列

最长递增子序列

  1. 状态表示
    dpi 表示以i位置为结尾, 最长的递增子序列
  2. 状态转移方程
    1.如果以i位置为开头 那么长度为1
    2.如果以i位置结尾, 由于不知道从前面哪一个位置取子序列, 所以要定义一个j(0 <= j <= i - 1), 找到以j位置结尾的所有子序列中, 最长的, 即dpj
  • dpi = dpj + 1
  1. 初始化
    可以先全部初始化为1
  2. 填表顺序
    从左往右
  3. 返回值
    返回dp表中的最大值
java 复制代码
class Solution {
    public int lengthOfLIS(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n];
        Arrays.fill(dp, 1);
        int ret = 1;
        for(int i = 1; i < n; i++){
            int max = 1;
            for(int j = 0; j < i; j++){
                if(nums[j] < nums[i]){
                    dp[i] = Math.max(dp[j] + 1, dp[i]);
                }
            } 
            ret = Math.max(dp[i], ret);
        }
        return ret;
    }
}

二. 摆动序列

摆动序列

  1. 状态表示
    fi 表示以i位置为结尾, 最后呈"上升"趋势最长的摆动序列
    gi 表示以i位置为结尾, 最后呈"下降"趋势最长的摆动序列
  2. 状态转移方程
    1.如果以i位置为开头 那么长度为1
    2.如果以i位置结尾, 由于不知道从前面哪一个位置取子序列, 所以要定义一个j(0 <= j <= i - 1), 找到以j位置结尾的所有子序列中, 最长的, 即dpj
    if(numsj < numsi){
    fi = Math.max(gj + 1, fi);
    }else if(numsj > numsi){
    gi = Math.max(fj + 1, gi);
    }
  3. 初始化
    可以先全部初始化为1
  4. 填表顺序
    从左往右
  5. 返回值
    返回dp表中的最大值
java 复制代码
class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n = nums.length;
        int[] f = new int[n];
        int[] g = new int[n];
        Arrays.fill(f, 1);
        Arrays.fill(g, 1);
        int ret = 1;
        for(int i = 1; i < n; i++){
            for(int j = 0; j < i; j++){
                if(nums[j] < nums[i]){
                    f[i] = Math.max(g[j] + 1, f[i]);
                }else if(nums[j] > nums[i]){
                    g[i] = Math.max(f[j] + 1, g[i]);
                }
                ret = Math.max(ret, Math.max(f[i], g[i]));
            }
        }
        return ret;
    }
}

三. 最长数对链

最长数对链

前置处理: 为了保证使用动态规划, 填表顺序是从左往右, 所以要先对数组进行排序, 只需要根据第一个数进行排序即可, 保证选择i位置时, 不会选到i后面的位置

  1. 状态表示
    dpi 表示以i位置为结尾, 最长数对链
  2. 状态转移方程
    1.如果以i位置为开头 那么长度为1
    2.如果以i位置结尾, 由于不知道从前面哪一个位置取子序列, 所以要定义一个j(0 <= j <= i - 1), 找到以j位置结尾的所有子序列中, 最长的, 即dpj
  • dpi = dpj + 1
  1. 初始化
    可以先全部初始化为1
  2. 填表顺序
    从左往右
  3. 返回值
    返回dp表中的最大值
java 复制代码
class Solution {
    public int findLongestChain(int[][] pairs) {
        Arrays.sort(pairs, (a, b) -> a[0] - b[0]);
        int n = pairs.length;
        int[] dp = new int[n];
        Arrays.fill(dp, 1);
        int ret = 1;
        for(int i = 1; i < n; i++){
            for(int j = 0; j < i; j++){
                if(pairs[j][1] < pairs[i][0]){
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
                ret = Math.max(dp[i], ret);
            }
        }
        return ret;
    }
}

四. 最长定差子序列

最长定差子序列

  1. 状态表示
    dpi 表示以i位置为结尾, 最长定差子序列
  2. 状态转移方程
    1.如果以i位置为开头 那么长度为1
    2.如果以i位置结尾, 由于不知道从前面哪一个位置取子序列, 所以要定义一个j(0 <= j <= i - 1), 找到以j位置结尾的所有子序列中, 最长的, 即dpj
    只需要找到在前面中, 大小为arri - difference 即可, 并且由于找的值是固定的, 那么我们只需要找到前面的其中一个即可

所以我们可以将dp表放在hash表中

  1. 初始化
    可以先全部初始化为1(在写代码时有技巧)
  2. 填表顺序
    从左往右
  3. 返回值
    返回dp表中的最大值
java 复制代码
class Solution {
    public int longestSubsequence(int[] arr, int difference) {

        //创建一个hash表, 在hash表中做dp
        Map<Integer, Integer> hash = new HashMap<>();//arr[i], dp[i]
        int ret = 1;
        for(int a : arr){
            hash.put(a, hash.getOrDefault(a - difference, 0) + 1);
            ret = Math.max(ret, hash.get(a));
        }
        return ret;
        //下面的方法超时
        // int n = arr.length;
        // int[] dp = new int[n];
        // Arrays.fill(dp, 1);
        // int ret = 1;
        // for(int i = 1; i < n; i++){
        //     for(int j = 0; j < i; j++){
        //         if(arr[i] - arr[j] == difference){
        //             dp[i] = Math.max(dp[i], dp[j] + 1);
        //         }
        //         ret = Math.max(ret, dp[i]);
        //     }
        // }
        // return ret;
    }
}

五. 最长的斐波那契子序列的长度

最长的斐波那契子序列的长度

  1. 状态表示

    dpi 表示以i位置为结尾, 最长斐波那契子序列的长度, 这样我们并不知道前面两个数是谁, 所以我们可以固定两个位置, 以i和j位置为结尾, 这样就可以找到k位置

  2. 状态转移方程

    如果找到k位置, 并且这个位置在i, j的前面, 那么dpij = dpki + 1

因为要频繁查找k的位置, 可以将arr中下标和值的映射关系放在hash表中

  1. 初始化
    可以先全部初始化为2
  2. 填表顺序
    从上往下
  3. 返回值
    返回dp表中的最大值, 但是如果找不到斐波那契序列, 那么应该返回0
java 复制代码
class Solution {
    public int lenLongestFibSubseq(int[] arr) {
        int n = arr.length;
        Map<Integer, Integer> hash = new HashMap<>();
        for(int i = 0; i < n; i++){
            hash.put(arr[i], i);
        } 
        int ret = 2;
        int[][] dp = new int[n][n];
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                dp[i][j] = 2;
            }
        }

        for(int i = 2; i < n; i++){
            for(int j = 1; j < i; j++){
                int a = arr[i] - arr[j];
                if(a < arr[j] && hash.containsKey(a))
                    dp[j][i] = dp[hash.get(a)][j] + 1;
                ret = Math.max(dp[j][i], ret);
            }
        }

        return ret == 2 ? 0 : ret;
    }
}
相关推荐
w_zero_one20 分钟前
算法--模拟
java·数据结构·算法
秋天的怀念_David44 分钟前
算法一千零一夜 · 第十三夜 幻方,武侠世界里的数学迷宫
java·程序人生·算法
秋天的怀念_David1 小时前
算法一千零一夜 · 第十四夜 幻方与奇妙的二进制
java·程序人生·算法
薛定e的猫咪1 小时前
(ICLR 2020)MAML 靠的不是快速学习,而是特征复用
人工智能·深度学习·学习·算法·机器学习
薛定e的猫咪2 小时前
(ICLR 2026)DF-META:面向 FJSP 的无导数元学习实例自适应调度
人工智能·深度学习·学习·算法·机器学习
小羊没烦恼!3 小时前
异步编程:线程概述及使用
java·开发语言·mysql·算法·c#
wanderist.4 小时前
202609CSP认证P3
算法
云淡风轻~窗明几净4 小时前
角谷猜想最接近严格证明的测试 Version1.1.1
人工智能·算法·机器学习·图论
机器学习之心4 小时前
PINN × SVM × NSGA-II × 熵权TOPSIS:一套跑通「建模—优化—决策」的多目标优化框架
算法·机器学习·支持向量机
别动我齐刘海5 小时前
机器人 C++ / 系统联调补强2——异常处理、Protobuf
linux·c++·人工智能·学习·算法·机器学习·机器人