一文吃透刷墙问题!LeetCode 256「粉刷房子」高频题两大解法多语言面试模板

部署运行你感兴趣的模型镜像

一、题目简介

LeetCode 256《粉刷房子》(Paint House)是动态规划的经典入门题。它模拟现实中粉刷房子,每个房子有三种颜色,粉刷不能相邻同色,要求最小化总花费。此题模型广泛出现在任务分配、调度与成本优化等实际场景。

二、题目描述

有n间房子排成一排,每个房子可以粉刷红、蓝、绿三种颜色之一。粉刷每个房子的花费不同,不能有相邻两间房子粉刷成同一种颜色,求最小总花费。

输入:costs,n行3列的二维数组,costs[i][j]表示粉刷第i个房子为颜色j(0红1蓝2绿)的花费。

三、示例分析

示例1:

输入: costs = [[17,2,17],[16,16,5],[14,3,19]]
输出: 10

解释:

  • 房子1涂蓝2,房子2涂绿5,房子3涂蓝3,2+5+3=10

四、解题思路与详细步骤

方案一:动态规划(自底向上/表格法)

步骤详解
  1. 定义dp[i][j]为粉刷前i+1间房,第i间房涂颜色j的最小花费。
  2. 初始值:dp[0][j] = costs[0][j]
  3. 状态转移:dp[i][j] = costs[i][j] + min(dp[i-1][k]),k≠j(即上一个房子的颜色不能和当前一样)
  4. 答案为最后一间房三种颜色的最小值:min(dp[n-1][0], dp[n-1][1], dp[n-1][2])
优缺点
  • 直观易懂,易扩展为更多颜色。
  • 空间O(n)可进一步优化到O(1)。

方案二:动态规划(滚动变量/原地修改)

步骤详解
  1. 原地修改costs数组,每一行只依赖上一行三种颜色的最小值。
  2. 对每个房子和每种颜色,累加不能与上一个相同颜色的最小花费。
  3. 结束后直接取costs[-1]三种颜色的最小值。
优缺点
  • 空间O(1),无需额外数组,适合大数据。
  • 可读性略低于标准dp。

五、代码实现(Python/Java/C++)

Python实现

解法一:常规DP表
def minCost(costs):
    if not costs: return 0
    n = len(costs)
    dp = [[0]*3 for _ in range(n)]
    dp[0] = costs[0][:]
    for i in range(1, n):
        for j in range(3):
            dp[i][j] = costs[i][j] + min(dp[i-1][(j+1)%3], dp[i-1][(j+2)%3])
    return min(dp[-1])
解法二:原地DP
def minCost(costs):
    if not costs: return 0
    for i in range(1, len(costs)):
        for j in range(3):
            costs[i][j] += min(costs[i-1][(j+1)%3], costs[i-1][(j+2)%3])
    return min(costs[-1])

Java实现

解法一:常规DP表
class Solution {
    public int minCost(int[][] costs) {
        if (costs == null || costs.length == 0) return 0;
        int n = costs.length;
        int[][] dp = new int[n][3];
        System.arraycopy(costs[0], 0, dp[0], 0, 3);
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < 3; j++) {
                dp[i][j] = costs[i][j] + Math.min(dp[i-1][(j+1)%3], dp[i-1][(j+2)%3]);
            }
        }
        return Math.min(Math.min(dp[n-1][0], dp[n-1][1]), dp[n-1][2]);
    }
}
解法二:原地DP
class Solution {
    public int minCost(int[][] costs) {
        if (costs == null || costs.length == 0) return 0;
        for (int i = 1; i < costs.length; i++) {
            for (int j = 0; j < 3; j++) {
                costs[i][j] += Math.min(costs[i-1][(j+1)%3], costs[i-1][(j+2)%3]);
            }
        }
        int n = costs.length - 1;
        return Math.min(Math.min(costs[n][0], costs[n][1]), costs[n][2]);
    }
}

C++实现

解法一:常规DP表
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
    int minCost(vector<vector<int>>& costs) {
        if (costs.empty()) return 0;
        int n = costs.size();
        vector<vector<int>> dp(n, vector<int>(3, 0));
        dp[0] = costs[0];
        for (int i = 1; i < n; ++i) {
            for (int j = 0; j < 3; ++j) {
                dp[i][j] = costs[i][j] + min(dp[i-1][(j+1)%3], dp[i-1][(j+2)%3]);
            }
        }
        return min({dp[n-1][0], dp[n-1][1], dp[n-1][2]});
    }
};
解法二:原地DP
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
    int minCost(vector<vector<int>>& costs) {
        if (costs.empty()) return 0;
        int n = costs.size();
        for (int i = 1; i < n; ++i) {
            for (int j = 0; j < 3; ++j) {
                costs[i][j] += min(costs[i-1][(j+1)%3], costs[i-1][(j+2)%3]);
            }
        }
        return *min_element(costs[n-1].begin(), costs[n-1].end());
    }
};

六、复杂度分析

  • 时间复杂度:O(n),每个房子遍历三种颜色
  • 空间复杂度:O(n)(解法一)或O(1)(解法二)

七、边界与细节注意

  • costs为空直接返回0
  • 只有一间房时直接选最小花费
  • 可原地修改节省空间
  • 可以类推到更多颜色和“圆环型”房子

八、模拟面试环节及答案

1. 问:原地DP为何不会影响答案?

答:
每次只用到上一行的值,且计算当前行时不会用到被覆盖的数据,因此可以安全原地修改。

2. 问:如果有k种颜色如何扩展?

答:
只需将3替换为k,遍历颜色时避开上一次相同的颜色,代码结构完全兼容。

3. 问:房子是首尾相连(圆环)时怎么办?

答:
可将问题拆成3个子问题,分别以三种颜色为起点和终点不同时,取最小值(或用环形DP)。

4. 问:如果要求输出具体的染色方案?

答:
可在DP时记录路径,或者逆推最小花费对应的染色选择,回溯方案。

5. 问:能否用贪心法?

答:
不能。贪心只考虑当前最优,无法保证全局最优(局部最小花费后续可能导致不可选),需动态规划。

九、总结升华

本题是动态规划与状态转移建模的入门代表。掌握DP与空间优化技巧后,不仅能高效解决本题,还能迁移至多颜色、多任务分配等场景,提升整体算法思维。

您可能感兴趣的与本文相关的镜像

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据分析螺丝钉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值