1473-粉刷房子 III

题目

1473. 粉刷房子 III - 力扣(LeetCode) (leetcode-cn.com)

思路

解决这类搜索问题最重要的部分是考虑如何将问题切分成类似的规模更小的问题, 对于本题, 存在三个决定问题规模的量: 房子数目、颜色数目和街区数目。如何控制这些量来实现减小问题规模并同时使得大规模的问题能够利用小规模问题的结果呢?我们需要从最大规模的问题开始考虑,考虑最大规模的问题需要依赖哪些小问题的答案来解决。

最大规模的问题需要涂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;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值