一题了解动态规划 —— 256. 粉刷房子(Leet Code 第八天)

在这个上面卡住了,去稍微看了些动态规划的知识。自己一直以来对于动态规划都不熟悉,自然而然这个题目也就解不出来。
为什么动态规划叫动态规划
看到一个解释:动态规划可将一个活动过程分成若干个互相联系的阶段,在它的每一阶段都需要作出决策,从而使整个过程达到最好的活动效果。当然,各个阶段决策的选取不是任意确定的,它依赖于当前面临的状态,又影响以后的发展,当各个阶段决策确定后,就组成一个决策序列,因而也就确定了整个过程的一条活动路线。在得到最后结果的过程中需要维护一个表格,这个过程中为了得到最优解表格中的值是在变化的,所以称为动态规划。还有一种说法就是动态规划主要用于求解以时间划分阶段的动态过程的优化问题,与之相对应的还有静态规划如线性规划。
动态规划和强化学习
当我看完相关的视频和简介后看见弹幕说和强化学习有点像,突然这么一想确实也是,动态规划就是不断地改变这个dp数组,根据dp数组得到一个最优的路径,就好像强化学习中的Q-learning,依赖Q-table来解决机器人的走迷宫问题。
基础知识
动态规划
五个核心步骤

  • dp数组以及数组下标的含义
  • 递推公式
  • dp数组如何初始化
  • 遍历顺序
  • 打印数组(为了检查是否有错误)
    题目
    在这里插入图片描述
    解决思路
  1. 首先寻找需要解决问题的前后依赖关系
    刷N个房子最小花费等于刷N-1个房子的最小花费+刷最后一个房子的最小花费
    因为题目限制了相邻两个房子的颜色不同,所以对于刷最后一个房子的最小花费就可能有三种情况
  • N-1个是红色,第N个就只能是min(蓝色,绿色)
  • N-1个是蓝色,第N个就只能是min(红色,绿色)
  • N-1个是绿色,第N个就只能是min(红色,蓝色)
    所以就需要三个dp数组,分别用来表示
  • 当N-1个是红色时,刷N个的最小花费。
  • 当N-1个是绿色时,刷N个的最小花费。
  • 当N-1个是蓝色时,刷N个的最小花费。
    数组的下标表示刷第i个房子,数组的元素表示刷第i个房子时的最小花费,所以数组长度为N+1(因为第0个不用)
    这样前两步就明晰了。
  1. 递推公式
    对于三种情况,设三个数组为a,b,c,分别表示红,蓝,绿;因为要刷N个房间,所以:
    a[N] = a[N-1] + min(蓝色,绿色)
    b[N] = b[N-1] + min(红色,绿色)
    c[N] = c[N-1] + min(红色,蓝色)
  2. dp数组如何初始化
    因为本题是要求花费,而后一个花费依赖前面一个的花费,所以只需要将红,蓝,绿的dp数组的第一个初始化为cost[0][0], costs[0][1], costs[0][2]就行了。
  3. 遍历顺序
    因为要求刷N个房间的最小花费,所以遍历顺序是从前往后的(因为只有前面的花费求出来了才能求后面的花费
  4. 打印数组(验证与DEBUG)
    代码
class Solution {
    
    
    public int minCost(int[][] costs) {
    
    
        int[] red = new int[costs.length];
        int[] blue = new int[costs.length];
        int[] green = new int[costs.length];
        red[0] = costs[0][0];
        blue[0] = costs[0][1];
        green[0] = costs[0][2];
        for (int i = 1; i < costs.length; i++) {
    
    
        //因为最后一个房子粉刷时只有红色,绿色,蓝色三种情况,所以这就相当于枚举三种情况的所有的可能
            red[i] = costs[i][0] + Math.min(blue[i - 1], green[i - 1]);//当最后一个红色
            blue[i] = costs[i][1] + Math.min(red[i - 1], green[i - 1]);//。。。
            green[i] = costs[i][2] + Math.min(red[i - 1], blue[i - 1]);//。。。
        }
        int min = Math.min(red[costs.length - 1], blue[costs.length - 1]);
        return Math.min(min, green[costs.length - 1]);
    }
}

在这里插入图片描述
穿插一点:递归的时间复杂度
一次递归的复杂度*递归次数


优化
可以看到时间复杂度已经很小,空间复杂度还很高,因为用了三个大小为n的数组,空间复杂度为N,观察使用的数组情况,每一次使用只用到了之前的一个值,所以可以继续优化:只用长度为二的数组保留之前的值和现在的值就好。
代码:

class Solution {
    
    
    public int minCost(int[][] costs) {
    
    
        int[] red = new int[2];
        int[] blue = new int[2];
        int[] green = new int[2];
        red[0] = costs[0][0];
        blue[0] = costs[0][1];
        green[0] = costs[0][2];
        for (int i = 1; i < costs.length; i++) {
    
    
            red[1] = costs[i][0] + Math.min(blue[0], green[0]);
            blue[1] = costs[i][1] + Math.min(red[0], green[0]);
            green[1] = costs[i][2] + Math.min(red[0], blue[0]);
            red[0] = red[1];
            blue[0] = blue[1];
            green[0] = green[1];
        }
        return Math.min(Math.min(red[0], blue[0]), green[0]);
    }
}

在这里插入图片描述
说实话以前一直没怎么认真了解过动态规划,感觉这东西很难,系统了解了下感觉消除了畏难情绪,按照步骤来一步步分析就好了,还是蛮值得的。

猜你喜欢

转载自blog.csdn.net/m0_60388871/article/details/128726498