leetcode题号分别为: 256、265、1473
1. 粉刷房子 Paint House
假如有一排房子,共 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
代码1:
from typing import List class Solution: def minCost(self, costs: List[List[int]]) -> int: n = len(costs) dp = [[0] * 3 for _ in range(n)] dp[0][0], dp[0][1], dp[0][2] = costs[0][0], costs[0][1], 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]) # %% s = Solution() print(s.minCost(costs = [[17,2,17],[16,16,5],[14,3,19]])) print(s.minCost(costs = [[7,6,2]]))
代码2:
from typing import List class Solution: def minCost(self, costs: List[List[int]]) -> int: n = len(costs) dp0, dp1, dp2 = costs[0][0], costs[0][1], costs[0][2] for i in range(1, n): dp0, dp1, dp2 = min(dp1, dp2) + costs[i][0], min(dp0, dp2) + costs[i][1], min(dp0, dp1) + costs[i][2] return min(dp0, dp1, dp2) # %% s = Solution() print(s.minCost(costs = [[17,2,17],[16,16,5],[14,3,19]])) print(s.minCost(costs = [[7,6,2]]))
输出:
10
2
2. 粉刷房子 II Paint House-ii
假如有一排房子,共 n 个,每个房子可以被粉刷成 k 种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。
当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。 每个房子粉刷成不同颜色的花费是以一个 n x k 的矩阵来表示的。
例如,costs[0][0] 表示第 0 号房子粉刷成 0 号颜色的成本花费; costs[1][2] 表示第 1 号房子粉刷成 2 号颜色的成本花费,以此类推。 请你计算出粉刷完所有房子最少的花费成本。
示例:
输入:[[1,5,3],[2,9,4]]
输出:5
解释:将 0 号房子粉刷成 0 号颜色,1 号房子粉刷成 2 号颜色。最少花费: 1 + 4 = 5; 或者将 0 号房子粉刷成 2 号颜色,1 号房子粉刷成 0 号颜色。最少花费: 3 + 2 = 5。
进阶:您能否在 O(n*k) 的时间复杂度下解决此问题?
代码:
from typing import List class Solution: def minCostII(self, costs: List[List[int]]) -> int: n = len(costs) k = len(costs[0]) dp = [[0] * k for _ in range(n+1)] for i in range(1, n+1): # 找出前 i-1 个房子中,除第 j 种颜色外的最小花费和次小花费分别是多少 min_cost, sec_min_cost = float('inf'), float('inf') for j in range(k): if dp[i-1][j] < min_cost: sec_min_cost = min_cost min_cost = dp[i-1][j] elif dp[i-1][j] < sec_min_cost: sec_min_cost = dp[i-1][j] # 根据前面计算的最小花费和次小花费,更新 dp[i][j] for j in range(k): if dp[i-1][j] == min_cost: dp[i][j] = sec_min_cost + costs[i-1][j] else: dp[i][j] = min_cost + costs[i-1][j] return min(dp[n]) # %% s = Solution() print(s.minCostII([[1,5,3],[2,9,4]]))
输出:
5
3. 粉刷房子 III Paint House-iii
在一个小城市里,有 m 个房子排成一排,你需要给每个房子涂上 n 种颜色之一(颜色编号为 1 到 n )。有的房子去年夏天已经涂过颜色了,所以这些房子不可以被重新涂色。
我们将连续相同颜色尽可能多的房子称为一个街区。(比方说 houses = [1,2,2,3,3,2,1,1] ,它包含 5 个街区 [{1}, {2,2}, {3,3}, {2}, {1,1}] 。)
给你一个数组 houses ,一个 m * n 的矩阵 cost 和一个整数 target ,其中:
houses[i]:是第 i 个房子的颜色,0 表示这个房子还没有被涂色。
cost[i][j]:是将第 i 个房子涂成颜色 j+1 的花费。
请你返回房子涂色方案的最小总花费,使得每个房子都被涂色后,恰好组成 target 个街区。如果没有可用的涂色方案,请返回 -1 。
示例 1:
输入:houses = [0,0,0,0,0], cost = [[1,10],[10,1],[10,1],[1,10],[5,1]], m = 5, n = 2, target = 3
输出:9
解释:房子涂色方案为 [1,2,2,1,1]
此方案包含 target = 3 个街区,分别是 [{1}, {2,2}, {1,1}]。
涂色的总花费为 (1 + 1 + 1 + 1 + 5) = 9。
示例 2:
输入:houses = [0,2,1,2,0], cost = [[1,10],[10,1],[10,1],[1,10],[5,1]], m = 5, n = 2, target = 3
输出:11
解释:有的房子已经被涂色了,在此基础上涂色方案为 [2,2,1,2,2]
此方案包含 target = 3 个街区,分别是 [{2,2}, {1}, {2,2}]。
给第一个和最后一个房子涂色的花费为 (10 + 1) = 11。
示例 3:
输入:houses = [0,0,0,0,0], cost = [[1,10],[10,1],[1,10],[10,1],[1,10]], m = 5, n = 2, target = 5
输出:5
示例 4:
输入:houses = [3,1,2,3], cost = [[1,1,1],[1,1,1],[1,1,1],[1,1,1]], m = 4, n = 3, target = 3
输出:-1
解释:房子已经被涂色并组成了 4 个街区,分别是 [{3},{1},{2},{3}] ,无法形成 target = 3 个街区。
提示:
m == houses.length == cost.length
n == cost[i].length
1 <= m <= 100
1 <= n <= 20
1 <= target <= m
0 <= houses[i] <= n
1 <= cost[i][j] <= 10^4
代码:
from typing import List class Solution: def minCostIII(self, houses: List[int], cost: List[List[int]], m: int, n: int, target: int) -> int: # i第(几个-1)房子,j第(几个-1)颜色,k组成(几个-1)街区 dp = [[[float('inf') for _ in range(target)] for _ in range(n)] for _ in range(m)] if houses[0] == 0: # 第一个房子无颜色 for i in range(n): # 初始化第一行 dp[0][i][0] = cost[0][i] # 由于只有一个房子,所以,数量也只能为1,也就是k=0 else: # 有颜色则无法更改,代价也应该为0 dp[0][houses[0] - 1][0] = 0 for i in range(1, m): for j in range(n): # 当房子原来有颜色,那么不可改变颜色,只能更新原有颜色与之前颜色的代价,其他颜色跳过 if houses[i] != 0 and houses[i] != j + 1: continue for k in range(target): for loc in range(n): if loc == j: # 与前一个房子颜色相同,分块数量一样 dp[i][j][k] = min(dp[i][j][k], dp[i - 1][loc][k]) elif k > 0: # 与前一个颜色不同,分数数量增加了1 dp[i][j][k] = min(dp[i][j][k], dp[i - 1][loc][k - 1]) # 只有dp[i][j][k]有解且房子无原有颜色,才增加粉刷成本 if dp[i][j][k] != float('inf') and houses[i] == 0: dp[i][j][k] += cost[i][j] ans = float('inf') # 遍历,找到分块为target的最低代价 for j in range(n): ans = min(ans, dp[m - 1][j][target - 1]) return ans if ans < float('inf') else -1 # %% s = Solution() print(s.minCostIII(houses = [0,0,0,0,0], cost = [[1,10],[10,1],[10,1],[1,10],[5,1]], m = 5, n = 2, target = 3)) print(s.minCostIII(houses = [0,2,1,2,0], cost = [[1,10],[10,1],[10,1],[1,10],[5,1]], m = 5, n = 2, target = 3)) print(s.minCostIII(houses = [0,0,0,0,0], cost = [[1,10],[10,1],[1,10],[10,1],[1,10]], m = 5, n = 2, target = 5)) print(s.minCostIII(houses = [3,1,2,3], cost = [[1,1,1],[1,1,1],[1,1,1],[1,1,1]], m = 4, n = 3, target = 3))
输出:
9
11
5
-1