
本文旨在解决在N栋房屋中种植花卉的最小成本问题,其中每栋房屋可选择三种花色之一,且相邻房屋的花色不能相同。针对传统暴力枚举方案在N较大时效率低下、易导致内存溢出的问题,本文提出并详细阐述了基于动态规划的高效算法。通过定义状态、推导状态转移方程,并提供Python示例代码,演示了如何计算最小总成本以及如何重建最优花色选择路径,显著提升了算法性能。
假设一条街上有N栋房屋,每栋房屋的庭院可以选择种植三种不同颜色的花卉(例如,白色、黄色、红色)中的一种。每种花色在每栋房屋都有对应的种植成本。核心约束是:任何两栋相邻房屋不能种植相同颜色的花卉。我们的目标是找到一种花卉种植方案,使得N栋房屋的总种植成本最低。
例如,对于四栋房屋,其花卉成本矩阵如下:
| 房屋/花色 | 白色 | 黄色 | 红色 |
|---|---|---|---|
| H1 | 5 | 6 | 7 |
| H2 | 3 | 7 | 9 |
| H3 | 6 | 7 | 3 |
| H4 | 1 | 8 | 4 |
一个可能的最低成本方案是:H1种植黄色 (6),H2种植白色 (3),H3种植红色 (3),H4种植白色 (1),总成本为 6 + 3 + 3 + 1 = 13。
初学者可能会尝试使用暴力枚举法来解决此问题。一种常见的做法是利用itertools.product生成所有可能的颜色组合(例如,对于N栋房屋,每栋有3种颜色,则有$3^N$种组合),然后过滤掉所有相邻房屋颜色相同的无效组合,最后计算剩余有效组合的成本并找出最小值。
这种方法在N较小时尚可接受,但随着N的增大,组合数量呈指数级增长。当N超过20时,$3^{20}$是一个巨大的数字,生成和处理如此多的组合将导致:
因此,对于大规模问题,我们需要一种更高效的算法。
动态规划(Dynamic Programming, DP)是解决这类具有最优子结构和重叠子问题特性的优化问题的强大工具。本问题的核心思想是:要找到第i栋房屋的最优解,我们只需要知道第i-1栋房屋的最优解,并结合当前房屋的成本和约束条件。
我们可以定义三个变量来表示到当前房屋为止,以特定颜色结束的最小成本:
对于每一栋房屋,我们根据前一栋房屋的最小成本来更新当前房屋的最小成本。
假设当前房屋的白色、黄色、红色花卉成本分别为x, y, z。
在每次迭代中,我们用new_cost_white, new_cost_yellow, new_cost_red来更新prev_cost_white, prev_cost_yellow, prev_cost_red,从而为下一栋房屋的计算做准备。
对于第一栋房屋,其成本就是选择相应花卉的成本。我们可以将prev_cost_white, prev_cost_yellow, prev_cost_red初始化为0,这样在处理第一行数据时,它们将分别加上第一行对应的花卉成本。
遍历完所有房屋后,cost_white, cost_yellow, cost_red将分别代表以白色、黄色、红色结束的最小总成本。取这三个值中的最小值,即为整个街区花卉种植的最低总成本。
以下Python代码实现了上述动态规划逻辑,用于计算最小总成本:
def solve_min_cost(rows):
"""
计算在给定约束下,种植花卉的最小总成本。
Args:
rows: 一个列表的列表,其中每个子列表代表一栋房屋的三种花卉成本。
例如:[[5, 6, 7], [3, 7, 9], ...]
Returns:
int: 最小总成本。
"""
# 初始化,a, b, c 分别代表以颜色1, 2, 3结束的最小成本
# 初始值为0,在处理第一栋房屋时,会直接加上第一栋房屋的成本
cost_color1 = 0
cost_color2 = 0
cost_color3 = 0
for x, y, z in rows:
# 计算当前房屋选择颜色1时的最小成本
# 前一栋房屋不能选择颜色1,所以取颜色2或颜色3的最小成本
new_cost_color1 = min(cost_color2, cost_color3) + x
# 计算当前房屋选择颜色2时的最小成本
# 前一栋房屋不能选择颜色2,所以取颜色1或颜色3的最小成本
new_cost_color2 = min(cost_color1, cost_color3) + y
# 计算当前房屋选择颜色3时的最小成本
# 前一栋房屋不能选择颜色3,所以取颜色1或颜色2的最小成本
new_cost_color3 = min(cost_color1, cost_color2) + z
# 更新成本,为下一栋房屋的计算做准备
cost_color1, cost_color2, cost_color3 = new_cost_color1, new_cost_color2, new_cost_color3
# 所有房屋处理完毕后,取三种结束状态中的最小值
return min(cost_color1, cost_color2, cost_color3)
# 示例数据
rows = [
[5, 6, 7], # H1: white, yellow, red costs
[3, 7, 9], # H2: white, yellow, red costs
[6, 7, 3], # H3: white, yellow, red costs
[1, 8, 4] # H4: white, yellow, red costs
]
print(f"最小总成本: {solve_min_cost(rows)}") # 输出: 最小总成本: 13仅仅知道最小总成本通常是不够的,我们还需要知道具体是哪些花色组合达到了这个最小成本。为了实现路径重建,我们需要在动态规划过程中额外存储选择信息。
现在,我们的状态不仅仅是成本,还需要包含到达该成本的路径信息。我们可以将每个状态定义为一个元组 (cost, path_tuple),其中 cost 是最小成本,path_tuple 是一个表示路径的元组,它会指向前一个状态的路径。
在计算 new_cost_colorX 时,我们不仅比较 prev_cost_colorY 和 prev_cost_colorZ,还要记录是哪一个导致了最小值。
例如,当计算 new_state_color1 时: min_prev_state = min(state_color2, state_color3) (这里 min 操作会比较元组的第一个元素,即成本) new_state_color1 = (min_prev_state[0] + x, (1, min_prev_state[1])) 其中 (1, min_prev_state[1]) 表示当前房屋选择了颜色1,并且其前一个状态的路径是 min_prev_state[1]。这里的 1 可以代表颜色索引(例如,1代表白色,2代表黄色,3代表红色)。
当所有房屋处理完毕后,我们得到 state_color1, state_color2, state_color3。从中选择成本最低的那个作为最终状态 (final_cost, final_path_tuple)。然后,我们可以通过递归或循环的方式,沿着 final_path_tuple 中的链接回溯,逐层解构出完整的花色序列。由于路径是逆序存储的,最后需要将其反转。
def solve_min_cost_and_path(rows):
"""
计算在给定约束下,种植花卉的最小总成本,并返回具体的花色选择路径。
Args:
rows: 一个列表的列表,其中每个子列表代表一栋房屋的三种花卉成本。
Returns:
tuple: (int, list) 最小总成本和花色选择列表。
花色列表中的1, 2, 3分别代表三种颜色。
"""
# 初始化状态,每个状态包含 (成本, 路径)
# 路径使用元组链表表示,(当前颜色索引, 前一个路径元组)
# None 表示路径的起点
state_color1 = (0, None) # (cost, path_tuple)
state_color2 = (0, None)
state_color3 = (0, None)
# 辅助函数:扩展路径并更新成本
def extend_path(prev_state_a, prev_state_b, current_cost, color_index):
"""
根据前两种可能的结束状态和当前房屋的成本,计算新的状态。
"""
# 比较前两种状态的成本,选择更小的那个
sum_min_prev_state, path_min_prev_state = min(prev_state_a, prev_state_b)
# 返回新的状态:(新成本, 新路径)
# 新路径是 (当前颜色索引, 指向前一个最小路径的元组)
return sum_min_prev_state + current_cost, (color_index, path_min_prev_state)
for x, y, z in rows:
# 计算当前房屋选择颜色1, 2, 3时的状态
new_state_color1 = extend_path(state_color2, state_color3, x, 1)
new_state_color2 = extend_path(state_color1, state_color3, y, 2)
new_state_color3 = extend_path(state_color1, state_color2, z, 3)
# 更新状态
state_color1, state_color2, state_color3 = new_state_color1, new_state_color2, new_state_color3
# 所有房屋处理完毕后,选择成本最低的最终状态
min_sum, final_path_tuple = min(state_color1, state_color2, state_color3)
# 回溯重建路径
flat_path = []
current_path = final_path_tuple
while current_path:
color_index, current_path = current_path # 解构 (当前颜色, 前一个路径)
flat_path.append(color_index)
# 由于路径是逆序构建的,需要反转
return min_sum, flat_path[::-1]
# 示例数据
rows = [
[5, 6, 7], # H1
[3, 7, 9], # H2
[6, 7, 3], # H3
[1, 8, 4] # H4
]
min_cost, path = solve_min_cost_and_path(rows)
print(f"最小总成本: {min_cost}") # 输出: 最小总成本: 13
print(f"最优花色路径: {path}") # 输出: 最优花色路径: [2, 1, 3, 1] (黄色, 白色, 红色, 白色)动态规划方法提供了一种高效且可扩展的解决方案,完美解决了暴力枚举在N较大时遇到的性能瓶颈。
通过理解和应用动态规划,我们可以有效地解决许多复杂的优化问题,从而设计出高性能的算法。
以上就是优化房屋花卉种植成本:基于动态规划的高效解决方案的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号