「这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战」
在一个小城市里,有 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。
思路
这题好难,看了好久提示,才想到变化的哪几个状态。
- index:下标,表示第几个屋子,从0开始计数
- color:颜色,从0开始计数,和cost对应
- neighborhoods:前index个屋子连续颜色的组数
然后对三个状态进行规划,总结状态方程。
dp[i][j][k] 表示 [0,i]都已经涂上颜色了,第 i 个房子的颜色是 j,它属于第 k 个街区 的最小值。
对于houses[i - 1] 已经上色的(houses[i - 1] != 0), 则只会有dp[i][j][houses[i - 1]]存在dp[i][j][houses[i - 1]]的最小值为 前 i - 1个房子被分成j个社区,且第 i - 1号房子颜色为houses[i - 1] 或者 前 i - 1个房子被分成 j - 1个社区且第i - 1号房子颜色不为houses[i - 1]
class Solution {
public int minCost(int[] houses, int[][] cost, int m, int n, int target) {
int[][][] dp = new int[m+1][m+1][n+1];//index blocks nowcolor
for (int[][] ints : dp) {
for (int[] anInt : ints) {
Arrays.fill(anInt,99999999);
}
}
for (int i = 0; i < n+1; i++) {
dp[0][0][i]=0;
}
for (int i = 1; i < m+1 ; i++) {
for (int j = 1; j <= i ; j++) {
for (int k = 1; k < n+1 ; k++) {
if (houses[i-1]==0) {
for (int l = 1; l < n+1 ; l++) {
if (l!=k)
dp[i][j][k]=Math.min(dp[i-1][j-1][l]+cost[i-1][k-1],Math.min(dp[i-1][j][k]+cost[i-1][k-1],dp[i][j][k]));
}
}else {
if (k==houses[i-1])
dp[i][j][houses[i-1]] = Math.min(dp[i-1][j][houses[i-1]],dp[i][j][houses[i-1]]);
else
dp[i][j][houses[i-1]] = Math.min(dp[i-1][j-1][k],dp[i][j][houses[i-1]]);
}
}
}
}
int min =9999999;
for (int i = 0; i <n+1 ; i++) {
min=Math.min(dp[m][target][i],min);
}
return min==9999999?-1:min;
}
}