想要精通算法和SQL的成长之路 - 填充书架

想要精通算法和SQL的成长之路 - 填充书架

  • 前言
  • [一. 填充书架](#一. 填充书架)
    • [1.1 优化](#1.1 优化)

前言

想要精通算法和SQL的成长之路 - 系列导航

一. 填充书架

原题链接


题目中有一个值得注意的点就是:

  • 需要按照书本顺序摆放。
  • 每一层当中,只要厚度不够了,当前层最高的那一本书籍就视为本层的高度。

那么我们假设dp[i]: 代表从 book[0] 摆到 book[i] 的时书架的最小高度。

  • 假设最后一层的第一本书的下标是 j ,那么之前所有书本摆放的最小高度就是 dp[j-1]
  • 我们再计算出,下标在[j,i](最后一层)的书本中,高度最高的那一本书(同时满足厚度不超过shelfWidth),高度为maxHeight
  • 那么当前的最小总高度是 res = Max(dp[i-1]+maxHeight,res)。即之前的总高度+最后一层的最高高度。

我们递归,从后往前递归。入参为遍历的书本下标。

  1. 终止条件:下标 <0。代表没有书本了,停止递归。
  2. 递归做的事情:循环[0,i]之间的所有元素,从后往前把书本放入最后一层,一旦厚度超出,终止遍历。否则,计算当前层的最高高度以及最小总高。
java 复制代码
public class Test1105 {
    public int[][] books;
    public int shelfWidth;

    public int minHeightShelves(int[][] books, int shelfWidth) {
        this.books = books;
        this.shelfWidth = shelfWidth;
        return dfs(books.length - 1);
    }

    public int dfs(int i) {
        // 终止条件
        if (i < 0) {
            return 0;
        }
        int res = Integer.MAX_VALUE, maxHeight = 0, width = shelfWidth;
        for (int j = i; j >= 0; j--) {
            // 从后往前放书本
            width -= books[j][0];
            // 厚度不能超过 shelfWidth ,超过就代表放不下了
            if (width < 0) {
                break;
            }
            // 当前层最高高度
            maxHeight = Math.max(maxHeight, books[j][1]);
            // 更新总最低书架高度 = 上层最小总高度 + 当前层最高高度
            res = Math.min(res, dfs(j - 1) + maxHeight);
        }
        return res;
    }
}

这个解答其实对于用例比较多的情况,是会超时的。

1.1 优化

我们来看下上面代码的不好的点:

  • 每次dfs的时候,循环的范围是:[0,j]
  • 循环内部又每次调用了dfs递归,即dfs[j-1]

整个递归函数,只用到了一个索引的参数,我们可以发现,索引为1,2,3...的递归,被重复调用了非常多次。以当前索引为3为例:

  • 第一次递归范围:0,3
  • 第二次递归范围:0,2
  • 第三次递归范围:0,1
  • ...

那么我们可以用一个全局的变量去记录每次dfs返回的结果即可:

java 复制代码
public class Test1105 {
    public int[][] books;
    public int shelfWidth;
    // 缓存dfs的结果
    public int[] dfsCache;

    public int minHeightShelves(int[][] books, int shelfWidth) {
        this.books = books;
        this.shelfWidth = shelfWidth;
        // 初始化
        dfsCache = new int[books.length];
        // 给个初始值,-1代表没有被执行过,即没有缓存
        Arrays.fill(dfsCache, -1);
        return dfs(books.length - 1);
    }

    public int dfs(int i) {
        // 终止条件
        if (i < 0) {
            return 0;
        }
        // 如果是-1,代表这层值没有执行过,往下走。否则,说明有缓存了,直接返回
        if (dfsCache[i] != -1) {
            return dfsCache[i];
        }
        int res = Integer.MAX_VALUE, maxHeight = 0, width = shelfWidth;
        for (int j = i; j >= 0; j--) {
            // 从后往前放书本
            width -= books[j][0];
            // 厚度不能超过 shelfWidth ,超过就代表放不下了
            if (width < 0) {
                break;
            }
            // 当前层最高高度
            maxHeight = Math.max(maxHeight, books[j][1]);
            // 更新总最低书架高度 = 上层最小总高度 + 当前层最高高度
            res = Math.min(res, dfs(j - 1) + maxHeight);
        }
        // 缓存下当前结果
        dfsCache[i] = res;
        return dfsCache[i];
    }
}
相关推荐
羊羊小栈6 分钟前
基于「YOLO目标检测 + 多模态AI分析」的公共场所暴力安全智能检测分析预警系统
人工智能·算法·面试·毕业设计·大作业
土星云SaturnCloud16 分钟前
ResNet-50 图像分类算法在边缘微服务器上的部署与性能评测
服务器·人工智能·算法·边缘计算·resnet-50
Niuguangshuo19 分钟前
文本-音频时间戳对齐:5 个开源工具实测
算法·开源·音视频
Niuguangshuo23 分钟前
论文解读:RNN-Transducer,端到端流式 ASR 的骨架
算法·语音识别
GreenTea35 分钟前
🔥别再用压缩了,Codex、NVIDIA、DeepSeek、Uber 给出 Agent 上下文管理的新答案
前端·后端·算法
All for pursuit.1 小时前
【矩阵-2】240.搜索二维矩阵 II
数据结构·c++·算法·leetcode
6Hzlia1 小时前
【Classic 150 刷题计划】 LeetCode 209. 长度最小的子数组 | C++ 滑动窗口(毛毛虫算法)经典模板
c++·算法·leetcode
6Hzlia1 小时前
【Classic 150 刷题计划】 LeetCode 28. 找出字符串中第一个匹配项的下标 | C++ 滑动窗口与子串比对
c++·算法·leetcode
GreenTea10 小时前
vLLM 与 SGLang KV Cache 底层实现机制深度调研报告
前端·后端·算法
Bode_200210 小时前
多资源规划(含生产调度、库存管理及产能规划)的创新优化算法
算法·调度