1. 动态规划专题训练概述
今天进入算法训练营的第39天,我们继续深入动态规划这个既让人爱又让人恨的专题。动态规划作为算法领域的重头戏,其重要性怎么强调都不为过——从互联网大厂的算法面试到各类编程竞赛,DP问题永远占据着举足轻重的地位。本专题我们将聚焦几个经典DP问题及其变种,通过拆解问题本质、分析状态转移、优化空间复杂度这三个关键步骤,带你真正掌握动态规划的精髓。
我在刷题和面试辅导过程中发现,很多同学卡在动态规划问题上的根本原因,往往不是coding能力不足,而是缺乏系统化的解题框架。这次我们就用"庖丁解牛"的方式,把每个问题分解到最基础的思考单元,让你看到DP问题背后的通用模式。特别提醒:今天涉及的背包问题变种和股票买卖问题,是面试中出现频率最高的DP题型,务必吃透每个细节。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 完全背包问题深度解析
2.1 完全背包与01背包的本质区别
完全背包问题与基础01背包的核心区别在于物品的可重复选取。在LeetCode 518.零钱兑换II这个问题中,我们需要计算用给定面额的硬币凑成指定金额的组合数。这与传统的01背包不同——每种硬币可以无限使用,这直接影响了我们的状态转移方程设计。
我刚开始接触这个问题时,犯过一个典型错误:直接套用01背包的二维DP解法,结果发现计算出的组合数总是偏少。后来通过画状态转移图才明白,完全背包需要调整物品的遍历顺序。具体来说,在01背包中我们逆序遍历容量是为了防止重复计算,而完全背包恰恰需要正序遍历来允许重复选取。
python复制def change(amount, coins):
dp = [0] * (amount + 1)
dp[0] = 1
for coin in coins:
for i in range(coin, amount + 1):
dp[i] += dp[i - coin]
return dp[amount]
2.2 排列与组合的微妙差异
很多同学容易混淆LeetCode 377.组合总和IV与前面的零钱兑换问题。虽然题目描述相似,但前者求的是排列数,后者求的是组合数。这意味着[1,2]和[2,1]在377题中被视为不同的方案,而在518题中视为相同。
这个差异反映在代码上,只
