[Java 算法] 动态规划(4)

练习一 : 最长递增子序列

300. 最长递增子序列 - 力扣(LeetCode)

java 复制代码
class Solution {
    public int lengthOfLIS(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n];
        // 初始化:每个元素至少是长度为1的子序列
        Arrays.fill(dp, 1);
        int maxLen = 1;

        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                // 如果nums[i]可以接在nums[j]后面
                if (nums[i] > nums[j]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            maxLen = Math.max(maxLen, dp[i]);
        }
        return maxLen;
    }
}

算法原理

  1. DP 状态定义 : dp[i]:以 nums i 这个数字结尾的最长递增子序列长度。

  2. 初始化 : dp[i] = 1因为每个数字自己单独就是一个长度为 1 的递增子序列。

  3. 对于每个数 nums[i],看它前面所有的数 nums[j]只要 nums[j] < nums[i],说明 nums[i] 可以接在以 j 结尾的序列后面长度就变成:dp[j] + 1

练习二 : 摆动序列

376. 摆动序列 - 力扣(LeetCode)

java 复制代码
class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n = nums.length;
        int[] f = new int[n];
        int[] g = new int[n];
        int ret = 1;
        for(int i = 0;i<n;i++) f[i] = g[i] = 1;
        for(int i = 1;i<n;i++){
            for(int j = i-1;j>=0;j--){
                if(nums[j]<nums[i]){
                    f[i] = Math.max(f[i],g[j]+1);
                }
                if(nums[j]>nums[i]){
                    g[i] = Math.max(g[i],f[j]+1);
                }
                ret = Math.max(Math.max(f[i],g[i]),ret);
            }
        }
        return ret;
    }
}

算法原理

  1. 状态定义(你用了两个 dp 数组,非常标准)
    fi:以 nums i 结尾,且最后一步是上升(nums i > 前一个数)的最长摆动序列长度
    gi:以 nums i 结尾,且最后一步是下降(nums i < 前一个数)的最长摆动序列长度
  2. 初始化 fi = gi = 1
    每个元素自己就是一个长度为 1 的摆动序列。
  3. 状态转移(你写得完全正确!)
    (1)当 nums j < nums i → 上升 说明i 可以接在 j 的下降序列后面 fi = max(fi, gj + 1)
    (2)当 nums j > nums i → 下降 说明i 可以接在 j 的上升序列后面gi = max(gi, fj + 1)
    摆动序列要求:上升 ↔ 下降 交替
    所以:
    想上升,必须接在下降结尾后面
    想下降,必须接在上升结尾后面
  4. 答案
    遍历过程中 fi 和 gi 的最大值

练习三 : 最长递增子序列的个数

673. 最长递增子序列的个数 - 力扣(LeetCode)

.

java 复制代码
class Solution {
    public int findNumberOfLIS(int[] nums) {
        int n = nums.length;
        int[] len = new int[n]; // 以i结尾的最长递增子序列长度
        int[] cnt = new int[n]; // 以i结尾的最长递增子序列的个数
        // 初始化
        for (int i = 0; i < n; i++) {
            len[i] = 1;
            cnt[i] = 1;
        }

        int maxLen = 1; // 全局最长长度
        // 填dp表
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[j] < nums[i]) { // 严格递增
                    if (len[j] + 1 > len[i]) {
                        // 找到更长的序列,更新长度和计数
                        len[i] = len[j] + 1;
                        cnt[i] = cnt[j];
                    } else if (len[j] + 1 == len[i]) {
                        // 长度相同,累加计数
                        cnt[i] += cnt[j];
                    }
                }
            }
            // 更新全局最长长度
            maxLen = Math.max(maxLen, len[i]);
        }

        // 统计所有长度等于maxLen的个数之和
        int res = 0;
        for (int i = 0; i < n; i++) {
            if (len[i] == maxLen) {
                res += cnt[i];
            }
        }
        return res;
    }
}

算法原理

  1. 状态定义

leni:以 numsi 结尾的最长递增子序列的长度

cnti:以 numsi 结尾的、长度为 leni 的最长递增子序列的个数

  1. 初始化

leni = 1:每个元素自身是长度为 1 的子序列

cnti = 1:每个元素自身对应 1 个长度为 1 的子序列

  1. 状态转移

遍历每个 i,枚举所有 j < i:

若 numsj < numsi(满足严格递增):

若 lenj + 1 > leni:说明找到了更长的子序列,更新 leni = lenj + 1,同时 cnti = cntj(个数继承自 j)

若 lenj + 1 == leni:说明找到了长度相同的新子序列,累加计数 cnti += cntj

若 numsj >= numsi:不满足严格递增,跳过

  1. 最终结果

先遍历 len 数组,找到全局最长长度 maxLen

再遍历所有 i,累加所有 leni == maxLen 的 cnti,即为答案

相关推荐
utf8mb4安全女神2 分钟前
【Redis数据库】哨兵集群/redis集群/安装配置/主从复制/数据持久化操作/数据结构/安全限制/PHP redis/
linux·服务器·数据结构·数据库·redis·缓存
先吃饱再说39 分钟前
为什么堆能 O(log n) 插入?拆解完全二叉树的数组魔法
算法·排序算法
丘山望岳1 小时前
哈希——数据分类存储柜
数据结构·散列表
留白_1 小时前
【决策树】泰坦尼克号生存预测
算法·决策树·机器学习
hqzing1 小时前
鸿蒙 PC 底层开发技术详解(七):二进制自签名算法的实现
算法·华为·harmonyos
AI科技星1 小时前
超复数全域经济周期场与信息谱场——金融与密码学底层理论重构《0·1·∞三元一体全域超复数统一场论》系列全集(六一字不漏完整合订终版)
人工智能·算法·金融·密码学·拓扑学·乖乖数学·全域数学
AI科技星2 小时前
《01无穷全域信息场论:算子G与宇宙本体高维完备公理大典》
人工智能·python·算法·金融·乖乖数学·全域数学
Tim_102 小时前
【C++】013、空类占多少字节?
算法
Ricky_Theseus2 小时前
并查集:连通性问题
算法
Tairitsu_H3 小时前
[LC优选算法#18] 前缀和 | 除⾃⾝以外数组的乘积 | 和为K的⼦数组 | 和可被K整除的⼦数组
算法·前缀和·哈希算法