返回
粉刷房子:深入浅出理解动态规划状态机
后端
2023-10-20 23:51:55
前言
在算法和数据结构的学习中,动态规划 (DP) 是一个必不可少的概念。它是一种解决优化问题的强大方法,通过将问题分解成子问题并存储子问题的解决方案,从而避免重复计算。在本文中,我们将结合动态规划和状态机思想,深入浅出地解析一道经典的 LeetCode 题 - 「粉刷房子」。
问题分析
「粉刷房子」问题如下:
有 n 个房子排成一排,每个房子都可以被粉刷成红色、蓝色或绿色。粉刷每个房子需要的花费如下:
粉刷红色:costs[0][i]
粉刷蓝色:costs[1][i]
粉刷绿色:costs[2][i]
限制条件:
- 相邻的房子不能被粉刷成相同颜色。
- 求粉刷所有房子最小的总花费。
动态规划状态机
为了解决这个问题,我们可以使用动态规划状态机的方法。具体来说,我们可以定义一个二维数组 dp
,其中 dp[i][j]
表示粉刷前 i
个房子,并且第 i
个房子被粉刷成颜色 j
时的最小总花费。
状态转移方程
根据上述定义,我们可以推导出状态转移方程:
dp[i][0] = min(dp[i-1][1], dp[i-1][2]) + costs[0][i]
dp[i][1] = min(dp[i-1][0], dp[i-1][2]) + costs[1][i]
dp[i][2] = min(dp[i-1][0], dp[i-1][1]) + costs[2][i]
其中:
i
表示当前正在粉刷的房子编号。j
表示当前正在粉刷的颜色(0 表示红色,1 表示蓝色,2 表示绿色)。costs[j][i]
表示粉刷第i
个房子为颜色j
的花费。min(...)
函数返回最小值。
初始化
对于基础情况,我们可以初始化 dp
数组的第一行:
dp[0][0] = costs[0][0]
dp[0][1] = costs[1][0]
dp[0][2] = costs[2][0]
递推
从第二行开始,我们可以根据状态转移方程递推计算 dp
数组的剩余部分:
def minCost(costs):
n = len(costs)
dp = [[0] * 3 for _ in range(n+1)]
dp[0][0] = costs[0][0]
dp[0][1] = costs[1][0]
dp[0][2] = costs[2][0]
for i in range(1, n):
dp[i][0] = min(dp[i-1][1], dp[i-1][2]) + costs[0][i]
dp[i][1] = min(dp[i-1][0], dp[i-1][2]) + costs[1][i]
dp[i][2] = min(dp[i-1][0], dp[i-1][1]) + costs[2][i]
return min(dp[n-1])
代码示例
以下是使用 Python 实现的代码示例:
def minCost(costs):
# 初始化 dp 数组
n = len(costs)
dp = [[0] * 3 for _ in range(n+1)]
# 初始化第一行
dp[0][0] = costs[0][0]
dp[0][1] = costs[1][0]
dp[0][2] = costs[2][0]
# 递推计算剩余部分
for i in range(1, n):
dp[i][0] = min(dp[i-1][1], dp[i-1][2]) + costs[0][i]
dp[i][1] = min(dp[i-1][0], dp[i-1][2]) + costs[1][i]
dp[i][2] = min(dp[i-1][0], dp[i-1][1]) + costs[2][i]
# 返回最小总花费
return min(dp[n-1])
# 示例输入
costs = [[17,2,17],[16,16,5],[14,3,19]]
# 调用函数并打印结果
result = minCost(costs)
print(result) # 输出:5
总结
通过将动态规划和状态机思想结合起来,我们可以有效地解决「粉刷房子」问题。本文从问题分析到代码实现,循序渐进地讲解了这一经典题目,希望能够帮助读者深入理解动态规划的精髓。在实际工作中,掌握动态规划技术对于解决各类优化问题至关重要,希望本文能够为读者的算法学习之路增添一抹亮色。