1. 题目来源
2. 题目解析
是检验自己 dp
水平的一道好题。
题目大致意思就是,给了一串房子,每个房子都要被涂色,若房子初始有颜色,则不能被重新涂色,每次涂色都有相应的涂色花费,相邻相同颜色的房子我们称其为一段颜色。求将房子涂成 t
段颜色后的最小花费。
思路:
- 显然需要维护三个状态,前
i
个房子被涂成j
段,且第i
个房子被涂成第k
种颜色的花费。 - 状态定义:
f[i][j][k]
前i
个房子被涂成j
段,且第i
个房子被涂成第k
种颜色的最小花费。为了方便计算,房子编号从 0 开始,颜色编号从 1 开始。 - 答案: 在该种状态转移下应该为
min(f[m - 1][target][1~n])
,即为前m
个房子,分成target
段后,最后一个房子为n
种颜色中的任意一种的花费最小值即为答案。 - 初始化: 得考虑第一个房子是否有颜色。
- 若第一个房子有颜色,则
f[0][1][house[0]] = 0;
表示将第一个房子染成 1 段,且颜色为自身颜色的花费为 0,即保持原有自身颜色,无花费。 - 若第一个房子没有颜色,则他可以染成任意颜色,则
f[0][1][1~n]=cost[0][0~n-1]
,注意在此的下标关系。
- 若第一个房子有颜色,则
- 状态转移:
- 如果第
i
个房子有颜色,那么它有两种选择:- 用变量
u
枚举 上一个房子的n
种颜色。 - 第一种: 和与它同色的第
i-1
个房子组成一段,即f[i][j][k] = min(f[i][j][k], f[i-1][j][u]), k == u
- 第二种: 自己自成一段,即
f[i][j][k] = min(f[i][j][k], f[i-1][j-1][u])
。 - 这两种选择都没有涂颜色的花费,只是单纯的状态转移。
- 用变量
- 同理,如果第
i
个房子没有颜色,那么它也有两种选择,和上一个房子同色,和上一个房子不同色。这次需要枚举第i
个房子的k
种颜色,同时需要枚举第i-1
个房子的u
种颜色。同色、不同色时需要特殊处理一下,其实和上面的处理方式是一样的,但是此时需要额外加上涂色的花费。
- 如果第
本题考查 dp
的是相当经典的,有限集合的选取问题,同背包问题一样。按照经验就能将这个状态定义出来,三维的状态恰好能将问题描述清楚,状态转移确实也很朴素,跟着题目要求来就是了。
这也是第 192 场周赛的第 4 题,全场 AK
了 290 多位吧,确实 dp
基础扎实一点就能将这题搞定~
- 时间复杂度: O ( n 3 ) O(n^3) O(n3)
- 空间复杂度: O ( n 3 ) O(n^3) O(n3)
代码:
class Solution {
public:
int minCost(vector<int>& houses, vector<vector<int>>& cost, int m, int n, int target) {
const int INF = 1e8;
vector<vector<vector<int>>> f(m, vector<vector<int>>(target + 1, vector<int>(n + 1, INF)));
// 初始化,第 0 个房子是否有颜色
if (houses[0]) f[0][1][houses[0]] = 0;
else {
for (int i = 1; i <= n; i ++ )
f[0][1][i] = cost[0][i - 1]; // 第 0 个房子染色,f[] 第三纬下标从 1 开始,代表颜色类型,cost 从 0 开始
}
for (int i = 1; i < m; i ++ ) // 枚举第 i 个房子染色情况
for (int j = 1; j <= target; j ++ ) { // 枚举染色 j 段的情况
if (houses[i]) { // 如果 i 房子已经染色,则只能染成这种颜色,本段染色没有代价花费
int k = houses[i];
for (int u = 1; u <= n; u ++ )
if (k == u) f[i][j][k] = min(f[i][j][k], f[i - 1][j][u]); // 和颜色相同的上一段合并
else f[i][j][k] = min(f[i][j][k], f[i - 1][j - 1][u]); // 自成一段
} else { // 未染色,得给其染色,计算花费
for (int k = 1; k <= n; k ++ ) // 枚举i的染色种类
for (int u = 1; u <= n; u ++ ) // 和上面情况一样,枚举、i-1的染色的情况
if (k == u) f[i][j][k] = min(f[i][j][k], f[i - 1][j][u] + cost[i][k - 1]);
else f[i][j][k] = min(f[i][j][k], f[i - 1][j - 1][u] + cost[i][k - 1]);
}
}
int res = INF;
for (int i = 1; i <= n; i ++ ) res = min(res, f[m - 1][target][i]);
if (res == INF) res = -1;
return res;
}
};