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

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

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. 状态定义

len[i]:以 nums[i] 结尾的最长递增子序列的长度

cnt[i]:以 nums[i] 结尾的、长度为 len[i] 的最长递增子序列的个数

  1. 初始化

len[i] = 1:每个元素自身是长度为 1 的子序列

cnt[i] = 1:每个元素自身对应 1 个长度为 1 的子序列

  1. 状态转移

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

若 nums[j] < nums[i](满足严格递增):

若 len[j] + 1 > len[i]:说明找到了更长的子序列,更新 len[i] = len[j] + 1,同时 cnt[i] = cnt[j](个数继承自 j)

若 len[j] + 1 == len[i]:说明找到了长度相同的新子序列,累加计数 cnt[i] += cnt[j]

若 nums[j] >= nums[i]:不满足严格递增,跳过

  1. 最终结果

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

再遍历所有 i,累加所有 len[i] == maxLen 的 cnt[i],即为答案

相关推荐
旖-旎2 小时前
分治(计算右侧小于当前元素的个数)(7)
c++·学习·算法·leetcode·排序算法·归并排序
cxr8282 小时前
细胞球运动追踪的卡尔曼滤波与力场插值算法 —— 活体内微米级颗粒实时定位与轨迹预测系统
算法
炘爚2 小时前
C++(流类:istream /ostream/istringstream /ostringstream)
开发语言·c++·算法
倒酒小生2 小时前
日拱一卒,滴水穿石
数据结构
爱睡懒觉的焦糖玛奇朵2 小时前
【工业级落地算法之打架斗殴检测算法详解】
人工智能·python·深度学习·学习·算法·yolo·计算机视觉
练习时长一年2 小时前
后端开发常用的skill推荐
人工智能·算法·职场和发展
漂流瓶jz2 小时前
UVA-10384 推门游戏 题解答案代码 算法竞赛入门经典第二版
数据结构·算法·深度优先·题解·aoapc·算法竞赛入门经典·uva
汀、人工智能2 小时前
[特殊字符] 第1课:两数之和
数据结构·算法·链表·数据库架构··两数之和
cxr8283 小时前
卡尔曼滤波与力场插值算法代码框架
算法