多状态动态规划之粉刷房子

简介: 多状态动态规划之粉刷房子

1. 题目分析



题目链接选自力扣 : 剑指 Offer II 091. 粉刷房子

3bf08c2e43e3f5561906535af8098927.png

结合示例 1 来看 :

15f1135aff9d93943cf075926cbd9579.png


可以看到, 这个二维数组里的每一行代表一个房间, 有三个颜色, 每一行的 0 1 2 三列分别代表红、蓝、绿三个颜色. 里面的数值对应粉刷对应颜色的所需要的花费.


相邻房间不能同色, 同时还要最终粉刷完所有房间的花费最小.


2. 状态表示



以 i 位置为结尾, 表示从第一个房间粉刷到第 i 个位置房间时所需要的最小花费.

对于最后 i 位置的房间, 其粉刷的房间颜色又有三种情况可以细分这个问题.


  1. i 位置房间最后粉刷为红色

这种情况我们用 dp[i][0] 表示, 即从第一个房间粉刷到第 i 房间结束时, i 位置房间粉刷为红色所需要的最小花费


  1. i 位置房间最后粉刷为蓝色

这种情况我们用 dp[i][1] 表示, 即从第一个房间粉刷到第 i 房间结束时, i 位置房间粉刷为蓝色所需要的最小花费


  1. i 位置房间最后粉刷为绿色

这种情况我们用 dp[i][2] 表示, 即从第一个房间粉刷到第 i 房间结束时, i 位置房间粉刷为绿色所需要的最小花费


这是一个典型的多状态动态规划问题, 而且比我们之前遇到的问题还多了一个状态表示.


3. 状态转移方程



这是一个多状态表示的问题, 因此它的状态转移方程也对应有多个. 分别来推导一下这三种情况.

  1. i 位置房间最后粉刷为红色

e66e147b8ecd57c472b58464052d3a7c.png

当 i 位置被粉刷成红色时, i - 1 位置则不能粉刷为红色, 只能为蓝色或者绿色. 因此最终的最低花费为, 从第一个房间开始粉刷到第 i - 1 个房间为蓝色时的最小花费, 对应状态表示为 dp[i-1][1]


或者 从第一个房间开始粉刷到第 i - 1 个房间为绿色时的最小花费, 对应状态表示为 dp[i-1][2].


取得是最低花费, 因此为 Math.min( dp[i-1][1], dp[i-1][2] ), 最后加上 i 位置固定粉刷为红色的花费 costs[i][0]


最终为 : dp[i][0] = Math.min( dp[i-1][1], dp[i-1][2] ) + costs[i][0]

  1. i 位置房间最后粉刷为蓝色


同理, dp[i][1] = Math.min( dp[i-1][0], dp[i-1][2] ) + costs[i][1]

  1. i 位置房间最后粉刷为绿色


同理, dp[i][2] = Math.min( dp[i-1][0], dp[i-1][1] ) + costs[i][2]


4. 初始化



初始化是为了防止填表时发生越界错误, 根据状态转移方程来看, dp[0][0] 和 dp[0][1] 以及 dp[0][2] 都需要初始化.


采取我们之前熟悉的新开一个格子的初始化方法.


这里需要申明, 由于 costs 数组里面放的是每个房间不同颜色的花费, 我们把这个花费给看成一个值放到一维 dp 表中进行分析. 这个一维的 dp 表就表示每一个房间.值则为每个房间的最小花费 ! 并非实际创建一个一维 dp 表 ! ! !


将原本旧 dp 表中的下标 0 位置数据放到新 dp 表中下标 1 位置的数据. 旧 dp 表中下标 1 位置的数据放到新 dp 表中下标 2 位置的数据. 以此类推. 这样就可以将原本的初始化( dp[0] )放到填表中 ( dp[1] ), 只需要初始化新的 dp 表的 dp[0] 即可.

489a85170aaf406dbecbadd11bf21467.png


那么, 新 dp 表的 dp[0] 位置该多少呢 ? 当只有一个房间的时候, 这个房间只有三种颜色可以粉刷, 红蓝绿. 最终的最小花费即为 costs[0][0]. 只受本身房间粉刷颜色花费影响, 因此新的 dp 表中下标为 0 位置的值也应该为 0 不能影响 下标为 1 位置的值.


5. 填表顺序



从状态转移方程来看, 想要填写 i 位置的最小花费, 就需要前一个位置的最小花费. 因此填表顺序为从左往右


6. 返回值



根据题意, 求解粉刷到 n 房间时的最小花费, dp 表新开了一格, 大小为 n + 1. 根据状态转移方程, 到 n 房间时有三种情况, 因此返回这三种情况的最小花费即可. 最小花费即为 : Math.min(dp[n][0], dp[n][1], dp[n][2] )


7. 代码演示



class Solution {
    public int minCost(int[][] costs) {
        // 1. 创建 dp 表
        int n = costs.length; // 一共有多少个房间
        int[][] dp = new int[n + 1][3]; // 里面存的是 const[i][j] 每个房间不同颜色的花费
        // 2. 初始化
        dp[0][0] = 0;
        // 3. 填写 dp 表
        for(int i = 1; i <= n; i++) {
            // 这里的 for(int j = 0; j < 3; j++) 可以不写, 因为每一行的 0 1 2 三列是题目固定的
            // 根据状态转移方程填写
            // 需要注意的是, 因为对开了一格, 原本 costs 数组 0 下标对应在新的 dp 表中的 1 位置.
            // 想要从 dp 表中找到原本在 costs 数组中的位置, 下标都需要 - 1
            dp[i][0] = Math.min( dp[i-1][1], dp[i-1][2] ) + costs[i-1][0];
            dp[i][1] = Math.min( dp[i-1][0], dp[i-1][2] ) + costs[i-1][1];
            dp[i][2] = Math.min( dp[i-1][0], dp[i-1][1] ) + costs[i-1][2];
        }
        // 4. 确认返回值
        return Math.min(Math.min(dp[n][0], dp[n][1]), dp[n][2]);
    }
}


相关文章
|
Java
java中锁的详解
java中锁的详解
156 0
|
缓存 JavaScript 前端开发
JavaScript进阶 - Web Workers与Service Worker
【7月更文挑战第6天】JavaScript的Web Workers和Service Worker增强了浏览器的性能处理和离线功能。Web Workers处理后台计算,减轻主线程压力,但通信有开销,受同源策略限制。Service Worker则能拦截网络请求,支持离线缓存和推送通知,但其生命周期和权限管理需谨慎处理。通过理解它们的工作原理和限制,开发者能创建更流畅、更健壮的Web应用。
472 0
|
SQL 监控 druid
【数据源】基于Druid来聊聊数据源
【数据源】基于Druid来聊聊数据源
935 0
|
资源调度 JavaScript 前端开发
Vue 最新动态!!!
Vue 最新动态!!!
Invalid bound statement (not found)错误【已解决】
Invalid bound statement (not found)错误【已解决】
2161 1
|
机器学习/深度学习 并行计算 Linux
linux搭建miniconda+cuda+pytoch深度学习环境
本文以图文结合的方式,详细记录了linux操作系统搭建miniconda+cuda+pytoch深度学习环境的步骤,供大家参考学习。
1489 1
|
消息中间件 存储 JSON
慕了,我要是早点看到这篇写 Kafka 的分区管理的文章就好了
Kafka可以将主题划分为多个分区(Partition),会根据分区规则选择把消息存储到哪个分区中,只要如果分区规则设置的合理,那么所有的消息将会被均匀的分布到不同的分区中,这样就实现了负载均衡和水平扩展。另外,多个订阅者可以从一个或者多个分区中同时消费数据,以支撑海量数据处理能力。
计算机组成原理各个缩写的含义
计算机组成原理各个缩写的含义
482 0
计算机组成原理各个缩写的含义
|
JavaScript
滑动图片验证
滑动图片验证
124 0
|
存储
LeetCode 329. Longest Increasing Path in a Matrix
给定一个整数矩阵,找出最长递增路径的长度。 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。
170 0
LeetCode 329. Longest Increasing Path in a Matrix