[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,即为答案

相关推荐
bIo7lyA8v25 分钟前
算法稳定性与数据分布的内在联系研究的技术8
算法
SHARK_pssm1 小时前
【数据结构——树与堆】
c语言·数据结构·经验分享·笔记
bIo7lyA8v1 小时前
算法可视化对教学与调试效率的影响分析的技术8
算法
hunterkkk(c++)1 小时前
优先队列启发式最短路径快速算法(优化SPFA)-HEAP_SPFA算法
算法
SiliconGazer2 小时前
第15届国赛满分代码解析(下)—— 运动轨迹算法、按键交互与完整状态机
算法·状态机·stc15f2k60s2·浮点运算·蓝桥杯国赛·运动轨迹、·向量分解
Navigator_Z2 小时前
LeetCode //C - 1096. Brace Expansion II
c语言·算法·leetcode
luj_17682 小时前
FreeDOS vs MS-DOS PC-DOS 对比解析
服务器·c语言·开发语言·经验分享·算法
RH2312112 小时前
2026.6.10 数据结构 二叉树
数据结构
笨笨没好名字2 小时前
Leetcode刷题python版第一周
python·算法·leetcode
Cthy_hy2 小时前
斯特林数:组合划分的递归经典,一二两类全解
python·算法·斯特林数