<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>动态规划 on Jeanphilo Blog</title><link>https://jeanphilogong.github.io/jeanblog/zh/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/</link><description>Recent content in 动态规划 on Jeanphilo Blog</description><generator>Hugo -- 0.168.0</generator><language>zh-cn</language><lastBuildDate>Thu, 08 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jeanphilogong.github.io/jeanblog/zh/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/index.xml" rel="self" type="application/rss+xml"/><item><title>LeetCode 152：乘积最大子数组，从负数反例推导双状态动态规划</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/152-maximum-product-subarray/</link><pubDate>Thu, 08 Oct 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/152-maximum-product-subarray/</guid><description>先枚举非空连续区间，再用负数反例推导最大与最小乘积状态，逐步得到常数空间的 Python 解法。</description></item><item><title>LeetCode 279：完全平方数，从剩余和推导动态规划</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/279-perfect-squares/</link><pubDate>Thu, 08 Oct 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/279-perfect-squares/</guid><description>从最大平方数优先的失败例子出发，逐步构建递归、记忆化与迭代动态规划，求组成整数所需的最少完全平方数。</description></item><item><title>LeetCode 139：单词拆分，从前缀是否可达开始</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/139-word-break/</link><pubDate>Tue, 22 Sep 2026 12:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/139-word-break/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：字符串 &lt;code&gt;s&lt;/code&gt; 和字符串列表 &lt;code&gt;wordDict&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wordDict&lt;/code&gt; 中的每个元素都是一个可以使用的单词；同一个单词可以重复使用&lt;/li&gt;
&lt;li&gt;输出：如果 &lt;code&gt;s&lt;/code&gt; 可以被拆成一个或多个字典单词，返回 &lt;code&gt;True&lt;/code&gt;；否则返回 &lt;code&gt;False&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;拆分必须覆盖整个字符串，不能跳过字符，也不能改变字符顺序&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：s = &amp;#34;leetcode&amp;#34;, wordDict = [&amp;#34;leet&amp;#34;, &amp;#34;code&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：True
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：&amp;#34;leetcode&amp;#34; 可以拆成 &amp;#34;leet&amp;#34; + &amp;#34;code&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：s = &amp;#34;applepenapple&amp;#34;, wordDict = [&amp;#34;apple&amp;#34;, &amp;#34;pen&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：True
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：&amp;#34;apple&amp;#34; + &amp;#34;pen&amp;#34; + &amp;#34;apple&amp;#34;；同一个单词可以重复使用
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：s = &amp;#34;catsandog&amp;#34;, wordDict = [&amp;#34;cats&amp;#34;, &amp;#34;dog&amp;#34;, &amp;#34;sand&amp;#34;, &amp;#34;and&amp;#34;, &amp;#34;cat&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：False
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：前面可以拼出一些片段，但最后不能覆盖完整字符串
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="约束"&gt;约束&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 300&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= wordDict.length &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= wordDict[i].length &amp;lt;= 20&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; 和 &lt;code&gt;wordDict[i]&lt;/code&gt; 只包含小写英文字母&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wordDict&lt;/code&gt; 中的单词互不相同&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-1先让已完成的前缀变得可见"&gt;Step 1：先让“已完成的前缀”变得可见&lt;/h2&gt;
&lt;p&gt;先只问一个具体问题：对于 &lt;code&gt;s = &amp;quot;leetcode&amp;quot;&lt;/code&gt;，在确认整串能否拆开之前，我们能不能先记录“已经拆完了哪一段”？&lt;/p&gt;</description></item><item><title>LeetCode 213：打家劫舍 II，环形拆成两个线性问题</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/213-house-robber-ii/</link><pubDate>Tue, 22 Sep 2026 10:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/213-house-robber-ii/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：整数数组 &lt;code&gt;nums&lt;/code&gt;，&lt;code&gt;nums[i]&lt;/code&gt; 表示第 &lt;code&gt;i&lt;/code&gt; 间房子的金额。&lt;/li&gt;
&lt;li&gt;房子首尾相连，不能偷相邻的两间房子，因此第 &lt;code&gt;0&lt;/code&gt; 间和第 &lt;code&gt;n - 1&lt;/code&gt; 间也不能同时偷。&lt;/li&gt;
&lt;li&gt;输出：在不触发警报的前提下，最多能偷到的金额。&lt;/li&gt;
&lt;li&gt;约束：&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 100&lt;/code&gt;，&lt;code&gt;0 &amp;lt;= nums[i] &amp;lt;= 1000&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [2,3,2]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：第 0 间和第 2 间首尾相邻，不能同时偷；选择金额 3 的房子。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [1,2,3,1]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：可以偷下标 0 和下标 2，也可以偷下标 1 和下标 3。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [1,2,3]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这篇只用 Python，先从最小的首尾冲突开始。&lt;/p&gt;
&lt;h2 id="step-1先处理首尾相邻的冲突"&gt;Step 1：先处理首尾相邻的冲突&lt;/h2&gt;
&lt;p&gt;先问一个具体问题：&lt;code&gt;nums = [2,3,2]&lt;/code&gt; 时，能不能沿用直线街道的做法，直接在整个数组上选择不相邻的房子？&lt;/p&gt;
&lt;p&gt;当前基线只有一条规则：相邻房子不能同时偷。若把这三个数当成直线，可能会把下标 &lt;code&gt;0&lt;/code&gt; 和下标 &lt;code&gt;2&lt;/code&gt; 当成彼此不相邻，从而错误地选择两端，得到金额 &lt;code&gt;4&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这在环形输入上就会失败：首尾也相邻，&lt;code&gt;0&lt;/code&gt; 和 &lt;code&gt;2&lt;/code&gt; 必须互斥。要消除这一个冲突，只增加一个分支规则：任何合法方案至少属于下面一个候选分支。&lt;/p&gt;</description></item><item><title>LeetCode 322：零钱兑换，从更小金额推出完全背包 DP</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/322-coin-change/</link><pubDate>Tue, 22 Sep 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/322-coin-change/</guid><description>从零钱兑换中的贪心失败和更小金额问题出发，逐步构建求最少硬币数量的完全背包动态规划。</description></item><item><title>LeetCode 300：最长递增子序列，从“以 i 结尾”推出序列 DP</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/300-longest-increasing-subsequence/</link><pubDate>Tue, 22 Sep 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/300-longest-increasing-subsequence/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：整数数组 &lt;code&gt;nums&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;输出：返回 &lt;code&gt;nums&lt;/code&gt; 的最长严格递增子序列长度&lt;/li&gt;
&lt;li&gt;子序列保持原数组中的相对顺序，但不要求连续，可以跳过元素&lt;/li&gt;
&lt;li&gt;如果选择的下标是 &lt;code&gt;i_1 &amp;lt; i_2 &amp;lt; ... &amp;lt; i_k&lt;/code&gt;，还必须满足 &lt;code&gt;nums[i_1] &amp;lt; nums[i_2] &amp;lt; ... &amp;lt; nums[i_k]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;子序列非空，因此至少选择一个元素&lt;/li&gt;
&lt;li&gt;约束：&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 2500&lt;/code&gt;，&lt;code&gt;-10^4 &amp;lt;= nums[i] &amp;lt;= 10^4&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [10,9,2,5,3,7,101,18]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：[2,5,7,101] 是一个长度为 4 的严格递增子序列；它不要求在原数组中连续。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [0,1,0,3,2,3]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：[0,1,2,3] 的长度为 4；`[0,1,3]` 也是合法但更短的选择。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这篇只用 Python，从“每个位置结尾能形成多长”这个局部问题开始，逐步写出可运行解法。&lt;/p&gt;
&lt;h2 id="step-1先问以哪个位置结束"&gt;Step 1：先问“以哪个位置结束？”&lt;/h2&gt;
&lt;p&gt;先看第一个例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;下标： 0 1 2 3 4 5 6 7
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;数值： 10 9 2 5 3 7 101 18
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;答案 &lt;code&gt;[2,5,7,101]&lt;/code&gt; 跳过了 &lt;code&gt;9&lt;/code&gt; 和 &lt;code&gt;3&lt;/code&gt;。当我们走到值为 &lt;code&gt;7&lt;/code&gt; 的位置时，不能只问“它左边相邻的数是谁”，而要问：&lt;/p&gt;</description></item><item><title>LeetCode 416：分割等和子集，从目标总和推到 0/1 背包倒序遍历</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/416-partition-equal-subset-sum/</link><pubDate>Tue, 22 Sep 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/416-partition-equal-subset-sum/</guid><description>从分割等和子集的题目压力出发，逐步建立 0/1 背包状态，并解释为什么一维压缩必须倒序遍历。</description></item><item><title>LeetCode 338：比特位计数，如何复用较小数字的结果</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/338-counting-bits/</link><pubDate>Wed, 15 Jul 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/338-counting-bits/</guid><description>从对 0..n 每个数字重复统计的 baseline 出发，推导 answer[i] = answer[i &amp;amp; (i - 1)] + 1，在线性时间内完成比特位计数。</description></item><item><title>LeetCode 198：打家劫舍，从偷或不偷推出一维 DP</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/198-house-robber/</link><pubDate>Sun, 03 May 2026 14:33:39 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/198-house-robber/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：整数数组 &lt;code&gt;nums&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums[i]&lt;/code&gt; 表示第 &lt;code&gt;i&lt;/code&gt; 间房子的金额&lt;/li&gt;
&lt;li&gt;不能偷相邻房子&lt;/li&gt;
&lt;li&gt;输出：返回最多能偷到的金额&lt;/li&gt;
&lt;li&gt;约束：&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 100&lt;/code&gt;，&lt;code&gt;0 &amp;lt;= nums[i] &amp;lt;= 400&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [1,2,3,1]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：偷下标 0 和下标 2，金额 1 + 3 = 4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [2,7,9,3,1]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：12
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：偷下标 0、2、4，金额 2 + 9 + 1 = 12
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这篇只用 Python，从这个二选一冲突推出一维 DP。&lt;/p&gt;
&lt;h2 id="从-1231-的相邻冲突开始"&gt;从 &lt;code&gt;[1,2,3,1]&lt;/code&gt; 的相邻冲突开始&lt;/h2&gt;
&lt;p&gt;看例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nums = [1,2,3,1]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果偷下标 &lt;code&gt;2&lt;/code&gt; 的房子，金额是 &lt;code&gt;3&lt;/code&gt;，那么下标 &lt;code&gt;1&lt;/code&gt; 和下标 &lt;code&gt;3&lt;/code&gt; 都不能偷。
如果不偷下标 &lt;code&gt;2&lt;/code&gt;，答案可能来自前面下标 &lt;code&gt;0..1&lt;/code&gt; 的最优结果。&lt;/p&gt;
&lt;p&gt;所以走到某一间房时，核心选择只有两个：&lt;/p&gt;</description></item><item><title>LeetCode 70：爬楼梯，从 dp[i] 含义推出一维 DP</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/70-climbing-stairs/</link><pubDate>Sun, 03 May 2026 14:33:39 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/70-climbing-stairs/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：整数 &lt;code&gt;n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;含义：爬到第 &lt;code&gt;n&lt;/code&gt; 阶楼顶&lt;/li&gt;
&lt;li&gt;每次可以爬 &lt;code&gt;1&lt;/code&gt; 或 &lt;code&gt;2&lt;/code&gt; 阶&lt;/li&gt;
&lt;li&gt;输出：返回到达楼顶的不同走法数量&lt;/li&gt;
&lt;li&gt;约束：&lt;code&gt;1 &amp;lt;= n &amp;lt;= 45&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：n = 2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：1+1，2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：n = 3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：1+1+1，1+2，2+1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这篇只用 Python，从 &lt;code&gt;dp[i]&lt;/code&gt; 的含义一步一步推出最终代码。&lt;/p&gt;
&lt;h2 id="从-n--3-的最后一步开始"&gt;从 &lt;code&gt;n = 3&lt;/code&gt; 的最后一步开始&lt;/h2&gt;
&lt;p&gt;先看最小能暴露转移的例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;n = 3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;到第 3 阶的最后一步只可能来自：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第 2 阶，再走 1 阶&lt;/li&gt;
&lt;li&gt;第 1 阶，再走 2 阶&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以“到第 3 阶的方法数”不是凭空算出来的，而是来自两个更小的位置。&lt;/p&gt;
&lt;h2 id="step-1先定义更小的问题"&gt;Step 1：先定义更小的问题&lt;/h2&gt;
&lt;p&gt;直接问“到第 &lt;code&gt;n&lt;/code&gt; 阶有几种走法”太大。先定义：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dp[i] = 到达第 i 阶的方法数
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意这里的 &lt;code&gt;i&lt;/code&gt; 是楼梯位置，不是数组下标含义上的第几个元素。&lt;/p&gt;</description></item><item><title>LeetCode 746：使用最小花费爬楼梯，从 top 位置推出 dp</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/746-min-cost-climbing-stairs/</link><pubDate>Sun, 03 May 2026 14:33:39 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/746-min-cost-climbing-stairs/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：整数数组 &lt;code&gt;cost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cost[i]&lt;/code&gt; 表示踩到第 &lt;code&gt;i&lt;/code&gt; 阶的代价&lt;/li&gt;
&lt;li&gt;每次可以爬 &lt;code&gt;1&lt;/code&gt; 或 &lt;code&gt;2&lt;/code&gt; 阶&lt;/li&gt;
&lt;li&gt;可以从下标 &lt;code&gt;0&lt;/code&gt; 或下标 &lt;code&gt;1&lt;/code&gt; 开始&lt;/li&gt;
&lt;li&gt;输出：返回到达楼顶的最小花费&lt;/li&gt;
&lt;li&gt;约束：&lt;code&gt;2 &amp;lt;= cost.length &amp;lt;= 1000&lt;/code&gt;，&lt;code&gt;0 &amp;lt;= cost[i] &amp;lt;= 999&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：cost = [10,15,20]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：15
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：从下标 1 开始，付 15 后直接到达 top
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：cost = [1,100,1,1,1,100,1,1,100,1]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="从-101520-的-top-位置开始"&gt;从 &lt;code&gt;[10,15,20]&lt;/code&gt; 的 top 位置开始&lt;/h2&gt;
&lt;p&gt;先看最小例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cost = [10,15,20]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;楼顶不是下标 &lt;code&gt;2&lt;/code&gt;，而是在最后一阶之后的位置，可以记为位置 &lt;code&gt;3&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;到达 top 位置 &lt;code&gt;3&lt;/code&gt; 的最后一步只可能来自：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;位置 &lt;code&gt;2&lt;/code&gt;，付 &lt;code&gt;cost[2]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;位置 &lt;code&gt;1&lt;/code&gt;，付 &lt;code&gt;cost[1]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这题最容易错的地方就在这里：我们要求的是“到达 top 的花费”，不是“到达最后一个下标的花费”。&lt;/p&gt;</description></item><item><title>LeetCode 53：最大子数组和，从 dp[i] 含义推出 Kadane</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/53-maximum-subarray/</link><pubDate>Sun, 03 May 2026 14:25:07 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/53-maximum-subarray/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：整数数组 &lt;code&gt;nums&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;输出：返回连续非空子数组的最大和&lt;/li&gt;
&lt;li&gt;子数组必须连续&lt;/li&gt;
&lt;li&gt;至少选一个元素&lt;/li&gt;
&lt;li&gt;约束：&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 10^5&lt;/code&gt;，&lt;code&gt;-10^4 &amp;lt;= nums[i] &amp;lt;= 10^4&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [-2,1,-3,4,-1,2,1,-5,4]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：6
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：[4,-1,2,1] 的和最大，为 6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [1]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这篇只用 Python，从 &lt;code&gt;dp[i]&lt;/code&gt; 的含义一步一步推出 Kadane 算法。&lt;/p&gt;
&lt;h2 id="从--21-34-的断点开始"&gt;从 &lt;code&gt;[-2,1,-3,4]&lt;/code&gt; 的断点开始&lt;/h2&gt;
&lt;p&gt;看一个前缀例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nums = [-2, 1, -3, 4]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果走到 &lt;code&gt;4&lt;/code&gt;，有两种选择：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把前面的某个连续子数组接到 &lt;code&gt;4&lt;/code&gt; 前面&lt;/li&gt;
&lt;li&gt;从 &lt;code&gt;4&lt;/code&gt; 自己重新开始&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里的关键不是“所有子数组怎么枚举”，而是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当我们决定一个最优子数组必须以当前位置结尾时，它的来源只有两种：接上前一个位置的最优结尾，或者从当前位置重新开始。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这就是一维 DP 的入口。&lt;/p&gt;
&lt;h2 id="step-1先定义一个更小的问题"&gt;Step 1：先定义一个更小的问题&lt;/h2&gt;
&lt;p&gt;直接问“整个数组的最大子数组和是多少”太大。我们先强加一个限制：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果子数组必须以第 &lt;code&gt;i&lt;/code&gt; 个元素结尾，它的最大和是多少？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个值记为 &lt;code&gt;dp[i]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这里的 &lt;code&gt;dp[i]&lt;/code&gt; 不是“前 &lt;code&gt;i&lt;/code&gt; 个元素里的最大子数组和”。它更窄：&lt;/p&gt;</description></item></channel></rss>