题目
思路
解决这类搜索问题最重要的部分是考虑如何将问题切分成类似的规模更小的问题, 对于本题, 存在三个决定问题规模的量: 房子数目、颜色数目和街区数目。如何控制这些量来实现减小问题规模并同时使得大规模的问题能够利用小规模问题的结果呢?我们需要从最大规模的问题开始考虑,考虑最大规模的问题需要依赖哪些小问题的答案来解决。
最大规模的问题需要涂m
个房子, 我们考虑第m
个房子的涂色方案(暂不考虑已经涂色的情况),其有n
种涂色方案,我们需要找到哪种涂色方案开销最小。为了找到开销最小的方案,我们尝试将第m
个房子染成所有n
种不同的颜色,然后求出第m
个房子颜色为n
时整个方案的最小开销,最后在从这n
个最小开销中找出最终方案的最小开销。
当我们确定第m
个房子的颜色为n
后,要求此方案最小开销,就需要考虑前m-1
个房子的涂色方案的最小开销,如果我们知道前m-1
个房子涂色方案的最小开销,结合第m
个房子涂色方案即可得到前m
个房子涂色方案的最小开销。但由于本题目中存在一个街区数目限制,并且第m
个房子的颜色和第m-1
个房子的颜色(相同或者不同)会影响最终的街区数量,因此我们需要求出前m-1
个房子在第m-1
个房子涂颜色n
,且街区数量为b
时的涂色方案的最小开销,这样我们才能判断第m
个房子涂不同颜色时的街区数量。
根据上述分析,我们需要求出第h
个房子在涂颜色c
,并且街区数目为b
时涂色方案的最小开销。我们使用动态规划解决该问题,即定义三维数组dp[h][c][b]
来保存第h
个房子在涂颜色c
,并且街区数目为b
时涂色方案的最小开销。
当方案不可行时(无法满足街区数目限制),设置开销为-1
。
要求dp[h][c][b]
, 我们需要遍历dp[h-1]
所有可能的颜色方案, 找到开销最小的方案(具体步骤键代码求min(dp[h-1]
部分))。
第h个房子涂颜色c的情况下
前h-1个房子最小涂色开销
dp[h][c][b] = min(dp[h-1]) + cost[h][c];
第h个房子涂色开销
(第h个房子已经涂色则为0)
当填充完dp
数组后,我们遍历dp
数组中最后一个房子在街区数为target
时所有涂色方案的开销,找出最小的开销即可。
代码
class Solution {
public:
int minCost(vector<int>& houses, vector<vector<int>>& cost, int m, int n, int target) {
// h表示house,c表示color,b表示block(街区)
int dp_cost[100][21][101];
// 填充第一个房子的状态
for(int c=1; c<n+1; c++) {
for(int b=1; b<target + 1; b++) {
// 方案不可行(街区数大于房子数目或者当前房子已经涂色,不能在涂成颜色c)
if (b > 1 || (houses[0] != 0 && houses[0] != c)) {
dp_cost[0][c][b] = -1;
continue;
}
// 第一个房子未涂色
if (houses[0] == 0){
// cost数组颜色编号从0开始,而我上边使用的编号从1开始
dp_cost[0][c][b] = cost[0][c-1];
// 第一个房子已经涂色
} else {
dp_cost[0][c][b] = 0;
}
}
}
for(int h=1; h<m; h++) {
for(int c=1; c<n+1; c++) {
for(int b=1; b<target + 1; b++) {
// 求dp_cost[h][c][b]
int cost_min = -1;
// 方案不可行(街区数大于房子数目或者当前房子已经涂色,不能在涂成颜色c)
if (b > (h + 1) || (houses[h] != 0 && houses[h] != c)) {
dp_cost[h][c][b] = -1;
continue;
}
// lc表示last color,上一个房子的颜色
// 求min(dp[h-1])
for(int lc=1; lc<n+1; lc++) {
// 和上一个涂不一样的颜色
int tmp_cost = -1;
if (lc != c && b > 1 && dp_cost[h-1][lc][b-1] != -1) {
tmp_cost = dp_cost[h-1][lc][b-1];
// 和上一个涂相同颜色
} else if (lc == c && dp_cost[h-1][lc][b] != -1) {
// 当前房子已经涂色
tmp_cost = dp_cost[h-1][lc][b];
}
if (tmp_cost != -1 &&
((cost_min == -1) || (cost_min > tmp_cost))) {
cost_min = tmp_cost;
}
}
// 如果当前房子需要涂色,则增加涂色的开销(cost_min == -1表示方案不可行则不用执行该步骤)
if (houses[h] == 0 && cost_min != -1) {
cost_min += cost[h][c-1];
}
dp_cost[h][c][b] = cost_min;
}
}
}
int ret = -1;
for(int c=1; c<n+1; c++) {
if (ret == -1 || (dp_cost[m-1][c][target] != -1 && dp_cost[m-1][c][target] < ret)) {
ret = dp_cost[m-1][c][target];
}
}
return ret;
}
};