2025-08-01:粉刷房子Ⅳ。用go语言,给定一个偶数个房屋排列在一条直线上,和一个大小为 n x 3 的二维数组 cost,其中 cost[i][j] 表

2025-08-01:粉刷房子Ⅳ。用go语言,给定一个偶数个房屋排列在一条直线上,和一个大小为 n x 3 的二维数组 cost,其中 costij 表示给第 i 个房屋涂第 j + 1 种颜色的花费。

房屋的涂色需要满足两个条件才算"美观":

  1. 相邻的房屋颜色不能相同。

  2. 距离两端位置相同的房屋(例如,第 0 和第 n-1 个房屋,第 1 和第 n-2 个房屋,以此类推)颜色也不能相同。

请计算出符合这些条件的涂色方案中,涂色总花费最低的方案所对应的最低开销。

2 <= n <= 100000。

n 是偶数。

cost.length == n。

costi.length == 3。

0 <= costij <= 100000。

输入: n = 4, cost = \[3,5,7,6,2,9,4,8,1,7,3,5]。

输出: 9。

解释:

最佳涂色顺序为 1, 2, 3, 2,对应的成本为 3, 2, 1, 3。满足以下条件:

不存在涂成相同颜色的相邻房屋。

位置 0 和 3 的房屋(等距于两端)涂成不同的颜色 (1 != 2)。

位置 1 和 2 的房屋(等距于两端)涂成不同的颜色 (2 != 3)。

使房屋看起来漂亮的最低涂色成本为 3 + 2 + 1 + 3 = 9。

题目来自力扣3429。

解题思路

由于房屋是对称的,我们可以将房屋分成对称的两部分来处理。对于第 i 个房屋和第 n-1-i 个房屋,我们需要确保它们的颜色不同,同时相邻房屋的颜色也不同。

动态规划状态定义

我们可以使用动态规划来解决这个问题。定义 f[i][j][k] 表示处理到第 i 对对称房屋时,前一个房屋的颜色为 j,当前对称对中第一个房屋的颜色为 k 时的最小总花费。

状态转移

对于每一对对称的房屋(第 i 个和第 n-1-i 个),我们需要选择它们的颜色:

  1. 第 i 个房屋的颜色不能与前一个房屋的颜色相同(相邻约束)。
  2. 第 i 个房屋和第 n-1-i 个房屋的颜色不能相同(对称约束)。
  3. 第 n-1-i 个房屋的颜色不能与下一个房屋的颜色相同(相邻约束,但下一个房屋的颜色尚未确定,因此需要在下一对处理时考虑)。

因此,对于每一对房屋,我们需要枚举前一个房屋的颜色和当前对称对中第一个房屋的颜色,然后选择满足约束的颜色组合,并更新动态规划状态。

初始化

初始时,没有前一个房屋,因此可以认为前一个房屋的颜色是"虚拟"的(即可以任意选择,但需要在后续处理中确保约束)。

结果计算

处理完所有对称对后,我们需要在所有可能的最后状态中选择最小的总花费。

具体步骤

  1. 对称处理:将房屋分成对称的两部分,例如对于 n=4,第 0 和第 3 个房屋为一对,第 1 和第 2 个房屋为一对。
  2. 动态规划填充
    • 对于每一对房屋,枚举前一个房屋的颜色和当前对称对中第一个房屋的颜色。
    • 确保当前对称对中两个房屋的颜色不同。
    • 确保当前对称对中第一个房屋的颜色与前一个房屋的颜色不同。
    • 更新动态规划状态。
  3. 结果提取:在动态规划表的最后一行中,找到最小的总花费。

时间复杂度和空间复杂度

  • 时间复杂度:对于每一对房屋,我们需要枚举前一个颜色和当前颜色,因此时间复杂度为 O(n * 3 * 3) = O(n)。因为 3 是颜色数量的常数。
  • 空间复杂度:动态规划表的大小为 O(n/2 * 3 * 3) = O(n),因为我们需要存储每一对房屋的状态。

示例解释

对于输入 n = 4, cost = [[3,5,7],[6,2,9],[4,8,1],[7,3,5]]

  1. 对称对为 (0, 3) 和 (1, 2)。
  2. 对于 (0, 3):
    • 选择颜色组合 (j, k) 满足 j != k。
    • 例如选择颜色 1 和 2,花费为 3 + 5 = 8。
  3. 对于 (1, 2):
    • 前一个颜色是 1 或 2(取决于 (0, 3) 的选择)。
    • 选择颜色组合满足不与前一个颜色相同,且两个颜色不同。
    • 例如如果前一个是 1,可以选择 (2, 3),花费为 2 + 1 = 3。
  4. 总花费为 8 + 3 = 11,但实际最优解是 9,说明需要更精细的状态转移。

实际最优解是:

  • (0, 3) 选择颜色 1 和 2,花费 3 + 5 = 8。
  • (1, 2) 选择颜色 2 和 3,花费 2 + 1 = 3。
  • 但相邻约束不满足(第 0 个颜色 1,第 1 个颜色 2;第 2 个颜色 3,第 3 个颜色 2)。
  • 更优解:
    • (0, 3) 选择颜色 1 和 2,花费 3 + 5 = 8。
    • (1, 2) 选择颜色 3 和 2,花费 9 + 1 = 10(不优)。
    • 或者:
      • (0, 3) 选择颜色 1 和 3,花费 3 + 7 = 10。
      • (1, 2) 选择颜色 2 和 1,花费 2 + 1 = 3。
      • 总花费 10 + 3 = 13。
    • 最优解是:
      • (0, 3) 选择颜色 1 和 2,花费 3 + 5 = 8。
      • (1, 2) 选择颜色 2 和 3,花费 2 + 1 = 3。
      • 但第 1 个和第 2 个颜色相同(2 和 2),违反对称约束。
    • 实际最优解:
      • (0) 颜色 1,花费 3。
      • (1) 颜色 2,花费 2。
      • (2) 颜色 3,花费 1。
      • (3) 颜色 2,花费 3。
      • 满足所有约束,总花费 3 + 2 + 1 + 3 = 9。

动态规划的优化

原始动态规划可能没有正确处理相邻约束和对称约束的结合。可能需要更精细的状态设计或优化。

Go完整代码如下:

go 复制代码
package main

import (
	"fmt"
	"math"
	"slices"
)

func minCost(n int, cost [][]int) int64 {
	f := make([][3][3]int, n/2+1)
	for i, row := range cost[:n/2] {
		row2 := cost[n-1-i]
		for preJ := range 3 {
			for preK := range 3 {
				res := math.MaxInt
				for j, c1 := range row {
					if j == preJ {
						continue
					}
					for k, c2 := range row2 {
						if k != preK && k != j {
							res = min(res, f[i][j][k]+c1+c2)
						}
					}
				}
				f[i+1][preJ][preK] = res
			}
		}
	}

	// 枚举所有初始颜色,取最小值
	res := math.MaxInt
	for _, row := range f[n/2] {
		res = min(res, slices.Min(row[:]))
	}
	return int64(res)
}

func main() {
	n := 4
	cost := [][]int{{3, 5, 7}, {6, 2, 9}, {4, 8, 1}, {7, 3, 5}}
	result := minCost(n, cost)
	fmt.Println(result)
}

Python完整代码如下:

python 复制代码
# -*-coding:utf-8-*-

import math

def min_cost(n, cost):
    # f[i][j][k] 表示涂第i组(对称位置的两间房)时,
    # 左边房屋颜色为 j,右边房屋颜色为 k 的最小花费
    f = [[[math.inf]*3 for _ in range(3)] for _ in range(n//2+1)]
    
    # 初始化 f[0],第一组前没有房屋,花费为0
    for j in range(3):
        for k in range(3):
            f[0][j][k] = 0
    
    for i in range(n//2):
        row_left = cost[i]
        row_right = cost[n - 1 - i]
        for preJ in range(3):
            for preK in range(3):
                res = math.inf
                for j, c1 in enumerate(row_left):
                    if j == preJ:
                        continue
                    for k, c2 in enumerate(row_right):
                        if k != preK and k != j:
                            cand = f[i][j][k] + c1 + c2
                            if cand < res:
                                res = cand
                f[i+1][preJ][preK] = res
    
    # 找出 f[n/2] 中的最小值
    res = math.inf
    for row in f[n//2]:
        res = min(res, min(row))
    return res

if __name__ == '__main__':
    n = 4
    cost = [
        [3, 5, 7],
        [6, 2, 9],
        [4, 8, 1],
        [7, 3, 5]
    ]
    print(min_cost(n, cost))
相关推荐
止语Lab1 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活1 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困1 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn1 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
用户298698530141 小时前
Python 实现 Excel 与 Markdown 互转的实用指南
后端·python·excel
用户77283104908401 小时前
krono-job:零侵入、单二进制交付的分布式任务调度平台(开源)
后端
Conan在掘金1 小时前
鸿蒙报错速查:Function lacks ending return statement,返回路径漏 return 就炸,根因 + 真解法
后端
人间凡尔赛2 小时前
AI-Native 云原生架构:2026 年从容器编排到智能体编排的范式革命
后端·云原生·架构
IT_陈寒2 小时前
Vue的响应式让我加班到凌晨3点,原来问题出在这
前端·人工智能·后端
卷无止境2 小时前
提升 Python 代码健壮性的方法大盘点
后端·python