发布时间:2026/7/21 23:57:17
力扣 LCR 091. 粉刷房子 —— 动态规划入门详解 引言动态规划是算法面试中的拦路虎许多初学者不知从何下手。今天讲解的「力扣 91. 粉刷房子」正是 DP 入门的绝佳练习题。它不像背包问题需要纠结容量维度而是用最朴素的二维 DP 表格清晰展示了状态定义、初始化、转移和返回的完整流程。无论你是算法新手还是面试备战者这篇文章都会带你一步步拆解题目让你真正理解 DP 的核心思想。让我们从一道题开始打通动态规划的任督二脉摘要本文详细解析力扣 91「粉刷房子」的 DP 解法。给定n×3成本矩阵求相邻颜色不同时的最小总花费。定义dp[i][j]为第i个房子刷颜色j的最小花费转移方程dp[i][j]costs[i][j]min(dp[i-1][k]) (k≠j)。通过示例手动推导 DP 表格并提供二维数组和 O(1) 滚动数组两种代码实现。重点总结三个易错点维度理解、返回值、三数取最小值。时间 O(n)空间可优化至 O(1)目录一、题目描述二、动态规划思路1. 为什么用 DP2. DP 数组的定义3. DP 数组的构造以示例为例4. 状态转移方程三、Java 代码实现四、代码优化空间压缩五、易错点总结特别重要⚠️ 注意点 1DP 数组的构造维度⚠️ 注意点 2返回值不是 dp[n-1][2]⚠️ 注意点 3三个数取最小值的写法六、复杂度分析总结一、题目描述假如有一排房子共n个每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。每个房子粉刷成不同颜色的花费是以一个n x 3的正整数矩阵costs来表示的。例如costs[0][0]表示第 0 号房子粉刷成红色的成本花费costs[1][2]表示第 1 号房子粉刷成绿色的花费以此类推。请计算出粉刷完所有房子最少的花费成本。示例 1输入: costs [[17,2,17],[16,16,5],[14,3,19]] 输出: 10 解释: 将 0 号房子粉刷成蓝色1 号房子粉刷成绿色2 号房子粉刷成蓝色。 最少花费: 2 5 3 10。示例 2输入: costs [[7,6,2]] 输出: 2二、动态规划思路1. 为什么用 DP这道题满足最优子结构第i个房子刷某种颜色的最小花费只依赖于第i-1个房子刷其他两种颜色的最小花费。因此我们可以用动态规划从前往后依次推导。2. DP 数组的定义我们定义一个二维数组dpdp[i][j]表示粉刷完前 i 个房子0 ~ i且第 i 个房子刷成颜色 j 时的最小总花费。其中i表示房子编号范围0 ~ n-1j表示颜色0红色1蓝色2绿色3. DP 数组的构造以示例为例输入costs [[17,2,17], [16,16,5], [14,3,19]]我们手动构造出dp数组房子 \ 颜色红色蓝色绿色0号房子172171号房子183372号房子211037推导过程初始化第一行第 0 号房子刷任意颜色花费就是它本身的成本。→dp[0] [17, 2, 17]第二行1号房子刷红色16 min(dp[0][1], dp[0][2]) 16 min(2,17) 18刷蓝色16 min(dp[0][0], dp[0][2]) 16 min(17,17) 33刷绿色5 min(dp[0][0], dp[0][1]) 5 min(17,2) 7第三行2号房子刷红色14 min(dp[1][1], dp[1][2]) 14 min(33,7) 21刷蓝色3 min(dp[1][0], dp[1][2]) 3 min(18,7) 10刷绿色19 min(dp[1][0], dp[1][1]) 19 min(18,33) 37最终最后一个房子2号房子的最小花费是min(21, 10, 37) 104. 状态转移方程dp[i][j] costs[i][j] min(dp[i-1][k]) 其中 k ≠ j也就是说当前房子刷颜色j的总花费 当前房子刷颜色j的成本 上一个房子刷另外两种颜色的较小值。三、Java 代码实现public class Main { public static void main(String[] args) { int[][] costs {{17, 2, 17}, {16, 16, 5}, {14, 3, 19}}; System.out.println(minCost(costs)); // 输出 10 } public static int minCost(int[][] costs) { int M costs.length; // 房子数量 int N 3; // 颜色数量红、蓝、绿 // dp[i][j]前 i 个房子第 i 个房子刷颜色 j 的最小总花费 int[][] dp new int[M][N]; // 1. 初始化第一行 for (int j 0; j N; j) { dp[0][j] costs[0][j]; } // 2. 从第二个房子开始递推 for (int i 1; i M; i) { for (int j 0; j N; j) { int prevMin; if (j 0) { // 当前刷红色上一个只能是蓝色或绿色 prevMin Math.min(dp[i-1][1], dp[i-1][2]); } else if (j 1) { // 当前刷蓝色上一个只能是红色或绿色 prevMin Math.min(dp[i-1][0], dp[i-1][2]); } else { // 当前刷绿色上一个只能是红色或蓝色 prevMin Math.min(dp[i-1][0], dp[i-1][1]); } dp[i][j] costs[i][j] prevMin; } } // 3. 返回最后一个房子的最小花费 return Math.min(dp[M-1][0], Math.min(dp[M-1][1], dp[M-1][2])); } }运行结果四、代码优化空间压缩因为dp[i]只依赖于dp[i-1]我们可以用一维数组滚动更新降低空间复杂度到O(1)public static int minCost(int[][] costs) { int n costs.length; int[] dp new int[3]; // 初始化第一行 dp[0] costs[0][0]; dp[1] costs[0][1]; dp[2] costs[0][2]; for (int i 1; i n; i) { int prev0 dp[0], prev1 dp[1], prev2 dp[2]; dp[0] costs[i][0] Math.min(prev1, prev2); dp[1] costs[i][1] Math.min(prev0, prev2); dp[2] costs[i][2] Math.min(prev0, prev1); } return Math.min(dp[0], Math.min(dp[1], dp[2])); }五、易错点总结特别重要⚠️ 注意点 1DP 数组的构造维度本题虽然只有一个“房子数量”维度但因为每个状态有 3 种颜色选择所以用二维数组dp[n][3]来记录。不要误以为需要“物品 容量”两个维度那是 01 背包的思路这里没有容量限制。⚠️ 注意点 2返回值不是dp[n-1][2]很多同学想当然地认为最后一个元素就是答案但这是错误的最后一个房子有三种可能颜色应该取三种颜色中的最小值return Math.min(dp[M-1][0], Math.min(dp[M-1][1], dp[M-1][2]));⚠️ 注意点 3三个数取最小值的写法Java 中Math.min()只支持两个参数取三个数最小值要嵌套Math.min(a, Math.min(b, c))六、复杂度分析时间复杂度O(n * 3) O(n)只需要遍历每个房子一次空间复杂度二维数组版O(n * 3) O(n)一维滚动数组版O(1)总结这道题是动态规划入门的经典题目核心思想是定义dp[i][j]表示第i个房子刷颜色j时的最小花费状态转移只依赖于前一个房子的两种颜色最后取最后一个房子的三种颜色中的最小值掌握了这道题后续遇到“打家劫舍”、“股票买卖”等经典 DP 问题思路也会更加清晰。希望这篇文章能帮助你更好地理解动态规划如果有问题欢迎留言讨论

相关新闻

2026/7/21 23:57:17

基于simulink的双向DC/AC接口变换器的系统效率

### 手把手教你学Simulink--直流微电网中双向DC/AC接口变换器的电压稳定控制 #### 摘要 随着能源转型的推进,直流微电网在分布式能源接入与供电可靠性提升方面发挥着日益重要的作用。双向DC/AC接口变换器作为直流微电网与交流电网能量交互的关键设备,其电压稳定控制对于保障…

2026/7/21 23:57:17

KVM主题:大页内存HugePages配置实践

KVM主题:大页内存HugePages配置实践 在虚拟化环境中,内存管理是影响性能的关键因素之一。KVM(Kernel-based Virtual Machine)作为Linux内核中的一个虚拟化模块,为虚拟机提供了高效的硬件虚拟化支持。为了进一步提升KVM…

2026/7/21 23:57:17

【Python自动化】安全库存阈值不同?库管/小白1个脚本预警

为什么你需要这个脚本 管多品类库存有多痛苦? 在仓库、仓库管理系统中管理货品时,出现令库管烦恼的问题是:把所有货品的安全库存阈值(即剩下多少件就预警且提示该补货/进货)设为统一标准,导致销量快的货总…

2026/7/22 2:18:18

真实攻防复盘:企业网站挂马入侵全过程拆解(从入侵到溯源清理)

📌 前言绝大多数新人只学靶场漏洞,从未见过真实入侵完整链路。真实黑客攻击不是单一漏洞点,而是漏洞利用→上传木马→权限维持→内网扩散→数据窃取的完整闭环。本文以一次真实企业官网挂马入侵事件做全流程复盘,带技术细节、阶段…

2026/7/22 2:18:18

HertzBeat无Agent监控:5分钟部署Linux服务器监控

1. 项目概述:HertzBeat与Linux监控的完美结合 作为一名运维工程师,我深知服务器监控的重要性。传统的监控方案如Zabbix、Prometheus虽然功能强大,但配置复杂、学习曲线陡峭。直到我遇到了HertzBeat这个开箱即用的开源监控工具,它彻…

2026/7/22 2:18:18

2026值得读的全球EMBA|头部院校中立择校测评

一、前言民营企业家、企业创始人选读EMBA,常陷入排名繁杂、特色模糊、适配性难判断的择校困境。本文从五大客观维度测评值得读的全球EMBA项目,分别是全球办学排名、院校办学定位、课程体系、学员圈层、产业资源。全程无商业推广、无夸大营销,…

2026/7/22 2:18:18

新电脑BIOS优化指南:提升性能的6个关键设置

1. 新电脑BIOS优化的必要性刚拿到新电脑时,大多数用户都会迫不及待地开机使用,却忽略了BIOS设置这个关键环节。BIOS(Basic Input/Output System)是计算机启动时加载的第一个软件,它负责初始化硬件并引导操作系统。出厂…

2026/7/22 2:18:18

2026国内EMBA偏向哪些行业|中立择校测评

不少民营企业家、企业创始人择校时,都会纠结国内EMBA偏向哪些行业,难以匹配自身赛道与发展需求。本文从五大客观维度横向测评,涵盖全球办学排名、院校办学定位、课程体系、学员圈层、产业资源。全程保持中立客观,无商业推广与营销…

2026/7/22 2:13:17

Mac生产力工具全攻略:从开发到设计的效率革命

1. Mac软件生态概述:从基础工具到效率革命作为一位深度使用Mac超过8年的设计师兼开发者,我见证了macOS生态从"小众选择"到"生产力标杆"的蜕变。与Windows不同,Mac软件更强调"少即是多"的设计哲学——优秀的Mac…

2026/7/20 6:33:00

Unity与Python本地通信:基于Flask的跨语言数据交换实战

1. 项目概述:为什么我们需要一个本地通信服务器?在游戏开发、数字孪生、仿真训练等众多领域,Unity作为强大的实时3D内容创作平台,其核心逻辑通常由C#驱动。然而,当我们需要进行复杂的数据分析、机器学习推理、科学计算…

2026/7/22 0:02:17

抓包代理链路下的 TLS 指纹变化分析 TLSFOWARD抓包工具

抓包代理链路下的 TLS 指纹变化分析:为什么调试环境会影响访问结果 摘要 在网页调试、接口联调、自动化巡检和授权采集排查中,抓包是常见手段。但很多开发者会遇到一个现象:正常访问页面时没有问题,一进入抓包或代理调试环境&…

2026/7/22 0:02:17

微信QQ聊天记录误删恢复与备份方案全指南

1. 聊天记录误删的常见场景与恢复思路作为一名长期关注数据安全的技术博主,我处理过上百起聊天记录误删的求助案例。手机误操作、系统升级失败、设备损坏是三大常见诱因。上周就遇到用户更新微信时断电,导致近两年的工作群聊记录全部消失的极端案例。不同…

2026/7/22 0:02:17

2026最新8款个人AI编程免费工具深度实测

作为一名全栈独立开发者,我最近半年一直在折腾副业项目,每个月在AI编程工具上的订阅费算下来其实也不算便宜。作为个人开发者,我们追求的就是用最少的成本获得最高效的开发体验。TRAE 基础版免费,字节跳动出品的国内首款 AI 原生 …

2026/7/21 20:02:44

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…