leetcode:256. 粉刷房子

题目来源

题目描述

在这里插入图片描述

class Solution {
public:
    /**
     * @param costs: n x 3 cost matrix
     * @return: An integer, the minimum cost to paint all houses
     */
    int minCost(vector<vector<int>> &costs) {
        // write your code here
    }
};

这是一个典型的序列型动态规划

序列型动态规划 VS 坐标型动态规划

  • 坐标型动态规划:是一个点,假设当前状态是(x,y),那么前一个状态是(x - 1, y - 1)
  • 序列型动态规划:是一条线,加入当前状态是[0…n],那么前一个状态是[0…n-1]

题目解析

  • 因为相邻两栋房子不能是同样的颜色,所以它是有后效性的,所以不能用贪心算法来解决。必须用dp来讲[后效性]处理成[无后效性]
  • dp本质上是一个递归,递归本质上是一个穷举。也就是说最后的一套房子一定会遍历到,而且必须要刷,只是刷的颜色不同。也就是说最后的决策是:刷红色、刷蓝色、刷绿色—》从三种决策中选出一种

动态规划

(1)确定状态

  • 最优策略是花费最小的策略
  • 最后一步:最优策略中房子N-1一定染成了红、蓝、绿中的一种。
  • 但是因为相邻两栋房子不能是同一种颜色,所以:
    • 如果最优策略中房子N-1是红色,那么房子N-2只能是蓝色或者绿色
    • 如果最优策略中房子N-1是绿色,那么房子N-2只能是蓝色或者红色
    • 如果最优策略中房子N-1是蓝色,那么房子N-2只能是绿色或者红色
  • 套用以前的思路:
    • 记录漆[0…N-1]栋房子的最小花费,就也需要记录漆[0…N-2]栋房子的最小花费
    • 根据套路:
      • 对于 f ( N ) f(N) f(N),它记录了 [ 0... N − 1 ] [0...N-1] [0...N1]房子的最小花费,f(N)返回最小花费
        • f ( N ) = c o s t [ N − 1 ] + f ( N − 1 ) , c o s t [ N − 1 ] f(N) = cost[N - 1] + f(N - 1), cost[N - 1] f(N)=cost[N1]+f(N1)cost[N1]为油漆第N-1栋房子是红色、蓝色、绿色中其中一个方案的最小花费
        • f ( N − 1 ) = c o s t [ N − 2 ] + f ( N − 1 ) , c o s t [ N − 2 ] f(N-1) = cost[N - 2] + f(N - 1), cost[N - 2] f(N1)=cost[N2]+f(N1)cost[N2]为油漆第N-2栋房子是红色、蓝色、绿色中其中一个方案的最小花费
        • 因为我们没有记录下来第N-2栋房子到底是什么颜色,所以房子N-2有可能和房子N-1撞色
    • 怎么解决。记录下颜色就可以了。
      • 如果要求最后的问题(油漆[0…N-1]房子的最小花费),就要知道:
        • 油漆[0…N-2]房子的最小花费,并且房子N-2是蓝色的花费&&
        • 油漆[0…N-2]房子的最小花费,并且房子N-2是绿色的花费&&
        • 油漆[0…N-2]房子的最小花费,并且房子N-2是红色的花费
      • 这样,我们就回答上面的问题了
        • 如果最优策略中房子N-1是红色,那么房子N-2只能是蓝色或者绿色
        • 如果最优策略中房子N-1是绿色,那么房子N-2只能是蓝色或者红色
        • 如果最优策略中房子N-1是蓝色,那么房子N-2只能是绿色或者红色
      • 即:求油漆[0…N-1]房子的最小花费并且房子N-1是红色、蓝色、绿色的最小花费,需要知道油漆[0…N-2]房子的最小花费并且房子N2是红色、蓝色、绿色的最小花费
  • 定义状态:设油漆[0…i-1]房子的最小花费并且房子i-1是红色、蓝色、绿色的最小花费分别为f[i][0]、f[i][1]、f[i][2]

(2)定义转移方程

  • 已知:设油漆[0…i-1]房子的最小花费并且房子i-1是红色、蓝色、绿色的最小花费分别为f[i][0]、f[i][1]、f[i][2]
  • 那么:

在这里插入图片描述

在这里插入图片描述
(3)初始条件和边界条件:

  • 初始条件: f[0][0] = f[0][1] = f[0][2] = 0,即不油漆任何房子的花费 (f[0][i]表示[0…-1]的数组)
  • 无边界条件

(4)计算顺序

  • 从上到下
  • 初始化 f[0][0] = f[0][1] = f[0][2] ,没有房子
  • 计算:f[1][0]、f[1][1]、F[2][1],油漆到第1栋房子
  • 计算:f[N-1][0]、f[N-1][1]、F[N-1][1],油漆到第N-1栋房子
  • 计算:f[N][0]、f[N][1]、F[N][1] ,油漆到第N栋房子
  • 返回值:std::min(f[N][0]、f[N][1]、F[N][1]);
class Solution {
public:
    int minCost(vector<vector<int>>& costs) {
        if (costs.empty() || costs[0].empty()) return 0;
        vector<vector<int>> dp = costs;
        for (int i = 1; i < dp.size(); ++i) {
            dp[i][0] += min(dp[i - 1][1], dp[i - 1][2]);
            dp[i][1] += min(dp[i - 1][0], dp[i - 1][2]);
            dp[i][2] += min(dp[i - 1][0], dp[i - 1][1]);
        }
        return min(min(dp.back()[0], dp.back()[1]), dp.back()[2]);
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值