【LeetCode 热题100】DP 实战进阶:最长递增子序列、乘积最大子数组、分割等和子集(力扣300 / 152/ 416 )(Go语言版)

简介: 本文深入解析三道经典的动态规划问题:**最长递增子序列(LIS)**、**乘积最大子数组** 和 **分割等和子集**。 - **300. LIS** 通过 `dp[i]` 表示以第 `i` 个元素结尾的最长递增子序列长度,支持 O(n²) 动态规划与 O(n log n) 的二分优化。 - **152. 乘积最大子数组** 利用正负数特性,同时维护最大值与最小值的状态转移方程。 - **416. 分割等和子集** 转化为 0-1 背包问题,通过布尔型 DP 实现子集和判断。 总结对比了三题的状态定义与解法技巧,并延伸至相关变种问题,助你掌握动态规划的核心思想与灵活应用!

🧠 DP 实战进阶:最长递增子序列、乘积最大子数组、分割等和子集(LeetCode 300 / 152 / 416)

在动态规划的学习路径中,这三道题常被视作进阶经典,它们分别对应不同的状态定义与优化思路:

  • 📈 300. 最长递增子序列(LIS):子序列类动态规划
  • ✖️ 152. 乘积最大子数组:带正负号波动的区间 DP
  • 🎯 416. 分割等和子集:0-1 背包问题的变种

📈 一、300. 最长递增子序列

📌 题目描述

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


💡 解题思路(一):动态规划

状态定义:

  • dp[i] 表示以 nums[i] 结尾的最长递增子序列长度。

状态转移方程:

for j := 0; j < i; j++ {
   
    if nums[i] > nums[j] {
   
        dp[i] = max(dp[i], dp[j] + 1)
    }
}

时间复杂度:O(n²)


✅ Go 实现

func lengthOfLIS(nums []int) int {
   
    n := len(nums)
    dp := make([]int, n)
    for i := range dp {
   
        dp[i] = 1
    }

    maxLen := 1
    for i := 1; i < n; i++ {
   
        for j := 0; j < i; j++ {
   
            if nums[i] > nums[j] {
   
                dp[i] = max(dp[i], dp[j]+1)
            }
        }
        maxLen = max(maxLen, dp[i])
    }
    return maxLen
}

func max(a, b int) int {
   
    if a > b {
   
        return a
    }
    return b
}

💡 解法(二):二分优化(贪心 + 二分)

维护一个数组 tailstails[i] 表示长度为 i+1 的递增子序列中末尾最小的值。

时间复杂度:O(n log n)


✖️ 二、152. 乘积最大子数组

📌 题目描述

给你一个整数数组 nums,找出一个乘积最大的连续子数组,返回该乘积。


💡 解题思路

因为负负得正的存在,我们需要同时记录当前的最大值和最小值:

  • dpMax[i]:以 i 结尾的最大乘积
  • dpMin[i]:以 i 结尾的最小乘积

状态转移:

dpMax[i] = max(nums[i], nums[i]*dpMax[i-1], nums[i]*dpMin[i-1])
dpMin[i] = min(nums[i], nums[i]*dpMax[i-1], nums[i]*dpMin[i-1])

✅ Go 实现(空间优化)

func maxProduct(nums []int) int {
   
    maxVal, minVal := nums[0], nums[0]
    res := nums[0]

    for i := 1; i < len(nums); i++ {
   
        tempMax := maxVal
        maxVal = max(nums[i], max(nums[i]*maxVal, nums[i]*minVal))
        minVal = min(nums[i], min(nums[i]*tempMax, nums[i]*minVal))
        res = max(res, maxVal)
    }
    return res
}

🎯 三、416. 分割等和子集

📌 题目描述

给定一个只包含正整数的非空数组,判断能否将其分成两个子集,使得两个子集的和相等


💡 解题思路

转换为 0-1 背包问题

  • 目标是找到一个子集,使其和为 sum/2
  • 状态定义:dp[j] 表示是否存在和为 j 的子集;
  • 状态转移:
dp[j] = dp[j] || dp[j - num]

✅ Go 实现

func canPartition(nums []int) bool {
   
    total := 0
    for _, num := range nums {
   
        total += num
    }
    if total%2 != 0 {
   
        return false
    }
    target := total / 2
    dp := make([]bool, target+1)
    dp[0] = true

    for _, num := range nums {
   
        for j := target; j >= num; j-- {
   
            dp[j] = dp[j] || dp[j-num]
        }
    }
    return dp[target]
}

🔚 总结与对比

题目 类型 状态定义 难点与技巧
300. 最长递增子序列 子序列 DP dp[i] 为以 i 结尾 LIS 长度 可二分优化
152. 乘积最大子数组 区间 DP 同时维护最大最小 处理正负数乘积
416. 分割等和子集 0-1 背包 dp[j] 为是否能组成 j 典型集合和划分

📘 写在最后

这三道题展示了动态规划在处理不同问题时的状态建模能力转移逻辑多样性。可以举一反三,比如:

  • LIS 拓展题:673. 最长递增子序列的个数
  • 背包拓展题:494. 目标和
  • 区间动态规划:42. 接雨水、91. 解码方法

目录
相关文章
|
3月前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
7月前
|
Go
【LeetCode 热题100】BFS/DFS 实战:岛屿数量 & 腐烂的橘子(力扣200 / 994 )(Go语言版)
本篇博客详细解析了三道经典的动态规划问题:198. 打家劫舍(线性状态转移)、279. 完全平方数与322. 零钱兑换(完全背包问题)。通过 Go 语言实现,帮助读者掌握动态规划的核心思想及其实战技巧。从状态定义到转移方程,逐步剖析每道题的解法,并总结其异同点,助力解决更复杂的 DP 问题。适合初学者深入理解动态规划的应用场景和优化方法。
260 0
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
316 6
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
188 6
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
215 4
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
417 2
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
329 3
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
388 1
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
413 7
|
Python
【Leetcode刷题Python】剑指 Offer 22. 链表中倒数第k个节点
Leetcode题目"剑指 Offer 22. 链表中倒数第k个节点"的Python解决方案,使用双指针法找到并返回链表中倒数第k个节点。
217 5

热门文章

最新文章