AlgoMooc
你已开通华为OD训练营权益,还差最后一步——完成入营激活(兑换课程 + 加飞书 + 登记服务群),即可解锁全部课程与专属服务。去激活 →
← OD 20 天课程

华为 OD 训练营 · 题解精讲

剑指OfferII 91. 粉刷房子

剑指OfferII 91. 粉刷房子

题目描述

假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。 当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的正整数矩阵 costs 来表示的。 例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。 请计算出粉刷完所有房子最少的花费成本。 示例 1: 输入: costs = [[17,2,17],[16,16,5],[14,3,19]] 输出: 10 解释: 将 0 号房子粉刷成蓝色,1 号房子粉刷成绿色,2 号房子粉刷成蓝色。 最少花费: 2 + 5 + 3 = 10。 示例 2: 输入: costs = [[7,6,2]] 输出: 2 提示: costs.length == n costs[i].length == 3 1 <= n <= 100 1 <= costs[i][j] <= 20

题目解析

解题思路

本题是典型的动态规划问题,要求相邻房子颜色不同,求最小总花费。参考代码使用二维 DP 数组 dp[i][j] 表示粉刷前 i 个房子(0-indexed)、且第 i 个房子刷颜色 j 时的最小总花费。由于相邻颜色不能相同,第 i 个房子刷颜色 j 时,第 i-1 个房子只能刷另外两种颜色,因此状态转移方程为:


dp[i][j] = min(dp[i-1][k]) + costs[i][j]   (k ≠ j)

最终答案为 min(dp[n-1][0], dp[n-1][1], dp[n-1][2])

关键步骤

1. 初始化 dp 数组:大小为 n×3,全部置 0。 2. base case:第 0 个房子的花费就是 costs[0] 本身,直接赋值:


   dp[0][0] = costs[0][0]
   dp[0][1] = costs[0][1]
   dp[0][2] = costs[0][2]

3. 状态转移:从 i=1 到 n-1 遍历每个房子,对每种颜色 j,取前一个房子另外两种颜色的 dp 值的最小值,加上当前房子刷颜色 j 的花费:


   dp[i][0] = min(dp[i-1][1], dp[i-1][2]) + costs[i][0]
   dp[i][1] = min(dp[i-1][0], dp[i-1][2]) + costs[i][1]
   dp[i][2] = min(dp[i-1][0], dp[i-1][1]) + costs[i][2]

4. 返回结果:取 dp[n-1] 中的最小值。

示意图(以 n=3 为例):


房子0: [17, 2, 17]   → dp[0] = [17, 2, 17]
房子1: [16,16, 5]    → dp[1][0] = min(2,17)+16 = 18
                      dp[1][1] = min(17,17)+16 = 33
                      dp[1][2] = min(17,2)+5  = 7
房子2: [14, 3, 19]   → dp[2][0] = min(33,7)+14 = 21
                      dp[2][1] = min(18,7)+3  = 10
                      dp[2][2] = min(18,33)+19= 37
最终 min(21,10,37) = 10

复杂度分析

  • 时间复杂度:O(n),只需遍历一次所有房子,每个房子进行常数次比较和加法。
  • 空间复杂度:O(n×3) = O(n),使用了一个 n×3 的二维数组。若采用滚动数组优化可降至 O(1),但参考代码未做此优化。

参考代码

class Solution:
    def minCost(self, costs: List[List[int]]) -> int:
        n = len(costs)

        # 1、初始化 dp 数组
        # dp[i][j] 表示第 i 个房子刷第 j 个颜色时的最少花费成本
        dp = [[0] * 3 for _ in range(n)]

        # 2、base case
        dp[0][0] = costs[0][0]
        dp[0][1] = costs[0][1]
        dp[0][2] = costs[0][2]

        # 3、填充数组
        for i in range(1, n):
            # 第 i 个房子刷第一个颜色
            dp[i][0] = min(dp[i - 1][1], dp[i - 1][2]) + costs[i][0]

            # 第 i 个房子刷第二个颜色
            dp[i][1] = min(dp[i - 1][0], dp[i - 1][2]) + costs[i][1]

            # 第 i 个房子刷第三个颜色
            dp[i][2] = min(dp[i - 1][0], dp[i - 1][1]) + costs[i][2]

        # 4、返回结果
        return min(dp[-1])
代码
class Solution:
    def minCost(self, costs: List[List[int]]) -> int:
        for i in range(1, len(costs)):
            costs[i][0] += min(costs[i - 1][1], costs[i - 1][2])
            costs[i][1] += min(costs[i - 1][0], costs[i - 1][2])
            costs[i][2] += min(costs[i - 1][0], costs[i - 1][1])
        return min(cost[-1])