Leetcode Link: 剑指 Offer II 091. 粉刷房子 - 力扣(LeetCode)
题目
假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。
当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的正整数矩阵 costs 来表示的。
例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。
请计算出粉刷完所有房子最少的花费成本。

解法一
思路:暴力递归
这一题的暴力递归比较简单,是从左到右的尝试模型。
在每个位置,需要知道上一个位置选择了哪一个颜色的涂料,然后在这个位置不选择。
注意 base case
题解:
class Solution:
def minCost(self, costs: List[List[int]]) -> int:
n = len(costs)
@functools.lru_cache(None)
def process(i, color):
# base case
if i == n:
return 0
res = float('inf')
for c in range(3):
# 和上一个位置的颜色一样跳过,不能用
if c == color:
continue
res = min(process(i+1, c) + costs[i][c], res)
return res
p1 = process(0, 0)
p2 = process(0, 1)
p3 = process(0, 2)
return min(p1, p2, p3)解法二
思路:动态规划
这一题的动态规划实际上要比暴力递归更好想
只要能够确定这是一个二维dp,基本上答案就出来了。每一个位置[i][color]受限于当前位置的颜色选择,不能和上一个位置的颜色重复,比较最小值就行
dp五步走:
dp[i][color]表示第i个位置涂color的涂料时,最小的花销- 递推公式:
dp[i][0]=min(dp[i-1][1], dp[i-1][2])+costs[i][0],其余两个颜色同理 - 初始值:0号位置的三种颜色
dp[0][0], dp[0][1], dp[0][2] - 递归方向:正常遍历,
for i in range(1, n)即可 - 尝试
题解:
class Solution:
def minCost(self, costs: List[List[int]]) -> int:
n = len(costs)
# [n ,color]
dp = [[float('inf') for _ in range(3)] for _ in range(n+1)]
# 初始化
dp[0][0] = costs[0][0]
dp[0][1] = costs[0][1]
dp[0][2] = costs[0][2]
for i in range(1, n):
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]
return min(dp[n-1][0], dp[n-1][1], dp[n-1][2])启发和联系
开始直接想 dp 的时候没想出来,就是卡在了不知道应该是几维 dp,写完暴力递归后,发现是二维 dp,没参考暴力递归的代码就写出了 dp 方法。
可见,dp维度的确定十分重要。