1473-粉刷房子 III

本文详细解析了LeetCode上的粉刷房子III问题的解决方案,通过动态规划策略,逐步缩小问题规模,考虑房子数量、颜色数量和街区数量的影响,最终找到涂色方案的最小开销。代码中展示了如何初始化状态并进行转移,以及如何找到最终答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

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;
    }
};
### 粉刷房子的二维动态规划解决方案 对于粉刷房子,存在一种时间复杂度为 \(O(n \times k^2)\) 的动态规划解法[^1]。此方法是对经典粉刷房子的一种自然延伸。 #### 动态规划表格定义 构建一个二维数组 `dp` ,其大小为 `[n][k]` 。这里 `n` 是房屋的数量,而 `k` 则代表可用的颜色数目。具体来说: - `dp[i][j]` 表达的是当第 `i` 座房子被涂上颜色 `j` 时,从前到这座房子为止所需的最低成本总和。 #### 初始化条件 为了确保计算过程中的准确性,在开始之前应当适当地初始化边界情况。通常情况下,如果只有一座房子,则它可以直接采用任意一种颜色,因此对应的初始开销即为给定的成本矩阵的第一行数据。 #### 状态转移方程 核心的状态转换逻辑如下所示: \[ dp[i][j]=\min_{l, l \neq j} (dp[i-1][l]) + costs[i][j]\] 这意呸着当前房子选择某种颜色后的最小累计费用等于前一座房子不选相同颜色下的最优解加上本房子选定颜色的实际消耗。 #### Python 实现示例 下面给出了一段基于上述理论框架实现的具体Python代码片段: ```python def minCostII(costs): if not costs or not costs[0]: return 0 N = len(costs) K = len(costs[0]) # Initialize DP table with the first house's painting cost. dp = [[0]*K for _ in range(N)] for color in range(K): dp[0][color] = costs[0][color] for house in range(1, N): for color in range(K): best_cost_without_same_color = float('inf') for prev_color in range(K): if prev_color != color: best_cost_without_same_color = min(best_cost_without_same_color, dp[house-1][prev_color]) dp[house][color] = best_cost_without_same_color + costs[house][color] return min(dp[-1]) ``` 通过这段程序可以有效地求解出所有可能组合里使得整体花费最少的结果集之一。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值