1. 问题背景与核心挑战
第一次看到LeetCode上这道"粉刷房子II"(Paint House II)时,我承认自己也被那个k给唬住了。题目描述很简单:有n栋排成一排的房子,每栋可以刷k种不同颜色,相邻房子颜色不能相同,给出每种颜色在每个房子上的粉刷成本,要求找出总成本最低的粉刷方案。
最直观的动态规划解法确实容易想到:用dp[i][j]表示第i栋房子刷第j种颜色时的最小总成本。状态转移方程也很直接:
code复制dp[i][j] = costs[i][j] + min(dp[i-1][c]),其中c != j
这意味着对于每栋房子i的每种颜色j,我们需要检查前一栋房子i-1的所有非j颜色的成本,取最小值。这样时间复杂度就是O(nk²),当k很大时(比如k=1000),这个复杂度就相当可观了。
关键问题:我们真的需要为每个颜色j都完整遍历前一栋的所有其他颜色吗?有没有办法避免这种"全量扫描"?
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 关键观察与优化思路
经过仔细分析,我发现了一个重要的优化点:对于每栋房子,其实我们只需要知道前一栋房子的两个关键信息:
- 前一栋房子的最小总成本是多少
- 这个最小值对应的颜色是什么
为什么这两个信息就够了?因为:
- 如果当前颜色j不等于前一栋的最小颜色,那么直接使用前一栋的最小值
- 如果当前颜色j等于前一栋的最小颜色,那么我们就需要用到前一栋的"次小值"
这个观察将问题简化为:在计算每栋房子时,我们只需要维护前一栋房子的最小和次小值及其对应的颜色,而不需要保留完整的k个状态。这样每栋房子的处理时间就从O(k²)降到了O(k)。
2.1 具体实现步骤
-
初始化:
- 第一栋房子的dp值就是其本身的粉刷成本
- 记录全局最小值和次小值及其颜色
-
递推计算:
- 对于第i栋房子的每种颜色j:
- 如果j不等于前一栋的最小颜色,则当前成本 = costs[i][j] + 前一栋的最小值
- 否则,当前成本 = costs[i][j] + 前一栋的次小值
- 在计算完第i栋的所有颜色后,更新当前的最小和次小值
- 对于第i栋房子的每种颜色j:
-
最终结果:
- 最后一栋房子的最小值就是全局最小总成本
2.2 复杂度分析
- 时间复杂度:O(nk)
