LeetCode每日一题——1473. 粉刷房子 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
难度:困难
解题思路
特殊处理第一个房子,然后前2栋房子到前mm栋房子用dp,一开始都需要初始化为无穷大,后面计算的时候需要利用这个来判断是否要更新值。注意点:一开始我以为i == 0 || j == 0 || k == 0三种0的情况不需要初始化为无穷大,后面发现自己错了,因为这种情况是永远达不到的,所以当我们当前所计算的问题要用到这个子问题时,就不能用其去更新,因为这种子问题方案自己本身就不行,不能用作计算原问题的解
AC代码
1 class Solution { 2 public int minCost(int[] houses, int[][] cost, int m, int n, int target) { 3 int[][][] f = new int[m + 1][n + 1][target + 1]; 4 5 for (int i = 0; i <= m; i++) { 6 for (int j = 0; j <= n; j++) { 7 for (int k = 0; k <= target; k++) { 8 if (i == 0 || j == 0 || k == 0) { 9 // 初始化为无穷大 10 f[i][j][k] = Integer.MAX_VALUE; 11 continue; 12 } 13 14 f[i][j][k] = Integer.MAX_VALUE; 15 16 // 注意:这里一定要特判i == 1,只有一栋房子的时候可以简单直接的求解 17 if (i == 1) { 18 // 判断这个房子是否已涂过颜色 19 // 这里我们只考虑组成1个街区,其他街区的都不可能组成,即为无穷大 20 if (houses[0] != 0) { 21 f[1][houses[0]][1] = 0; 22 } else { 23 for (int a = 1; a <= n; a++) { 24 f[1][a][1] = cost[0][a - 1]; 25 } 26 } 27 continue; 28 } 29 30 // 2-m 31 if (houses[i - 1] != 0) { 32 if (j != houses[i - 1]) { 33 continue; 34 } 35 // 讨论1-n种颜色 36 for (int a = 1; a <= n; a++) { 37 if (a == j) { 38 if (f[i - 1][a][k] == Integer.MAX_VALUE) { 39 continue; 40 } 41 f[i][j][k] = Math.min(f[i][j][k], f[i - 1][a][k]); 42 } else { 43 if (f[i - 1][a][k - 1] == Integer.MAX_VALUE) { 44 continue; 45 } 46 f[i][j][k] = Math.min(f[i][j][k], f[i - 1][a][k - 1]); 47 } 48 } 49 } else { 50 // 讨论1-n种颜色 51 for (int a = 1; a <= n; a++) { 52 if (a == j) { 53 if (f[i - 1][a][k] == Integer.MAX_VALUE) { 54 continue; 55 } 56 f[i][j][k] = Math.min(f[i][j][k], f[i - 1][a][k] + cost[i - 1][j - 1]); 57 } else { 58 if (f[i - 1][a][k - 1] == Integer.MAX_VALUE) { 59 continue; 60 } 61 f[i][j][k] = Math.min(f[i][j][k], f[i - 1][a][k - 1] + cost[i - 1][j - 1]); 62 } 63 } 64 } 65 } 66 } 67 } 68 69 // 选最小 70 int ans = Integer.MAX_VALUE; 71 for (int i = 1; i <= n; i++) { 72 ans = Math.min(ans, f[m][i][target]); 73 } 74 75 return ans >= Integer.MAX_VALUE ? -1 : ans; 76 } 77 }