华为OD机试_2025 B卷_书籍叠放(Python,200分)(附详细解题思路)
题目描述
书籍的长、宽都是整数对应 (l,w)。如果书A的长宽度都比B长宽大时,则允许将B排列放在A上面。现在有一组规格的书籍,书籍叠放时要求书籍不能做旋转,请计算最多能有多少个规格书籍能叠放在一起。
输入描述
输入:books = [[20,16],[15,11],[10,10],[9,10]]
说明:总共4本书籍,第一本长度为20宽度为16;第二本书长度为15宽度为11,依次类推,最后一本书长度为9宽度为10.
输出描述
输出:3
说明: 最多3个规格的书籍可以叠放到一起, 从下到上依次为: [20,16],[15,11],[10,10]
用例
输入 | [[20,16],[15,11],[10,10],[9,10]] |
输出 | 3 |
书籍叠放问题:最长严格递减子序列应用
核心解题思路
问题分析
题目要求找出能叠放在一起的最大书籍数量,规则是:
- 只有当书籍A的长宽都严格大于书籍B时,才能将B放在A上面
- 书籍不能旋转
- 需要找到一个书籍序列,使得序列中每个书籍的长宽都严格大于其后面的书籍
解决方案:排序+最长严格递减子序列
- 关键洞察:问题本质是寻找二维空间中的最长链,其中每个元素都严格大于链中下一个元素
- 排序预处理:
- 按长度降序排序(从大到小)
- 长度相同时,按宽度降序排序(从大到小)
- 问题转化:排序后问题简化为在宽度序列中寻找最长严格递减子序列
- 高效算法:使用二分查找维护最小末尾序列,时间复杂度O(n log n)
完整代码实现
import bisectdef max_books(books):"""计算最多能叠放的书籍数量参数:books: 二维列表,每个元素为[长, 宽]返回:最多能叠放的书籍数量"""# 处理空输入if not books:return 0# 1. 对书籍进行排序:长度降序,长度相同则宽度降序books.sort(key=lambda x: (-x[0], -x[1]))# 2. 提取所有书籍的宽度widths = [book[1] for book in books]# 3. 寻找宽度序列的最长严格递减子序列tail = [] # 存储每种长度递减子序列的最小末尾值for w in widths:# 如果当前宽度小于tail的最后一个元素,直接添加if not tail or w < tail[-1]:tail.append(w)else:# 二分查找第一个大于等于w的位置idx = bisect.bisect_left(tail, w)# 用w替换该位置的元素tail[idx] = wreturn len(tail)# 主程序
if __name__ == "__main__":# 读取输入import sysdata = sys.stdin.read().strip()# 处理输入格式:"[[20,16],[15,11],[10,10],[9,10]]"if not data:print(0)else:# 删除两端的方括号data = data[2:-2]# 分割书籍数据book_strs = data.split('],[') if data else []books = []for s in book_strs:# 分割长宽parts = s.split(',')if len(parts) == 2:l = int(parts[0])w = int(parts[1])books.append([l, w])# 计算并输出结果result = max_books(books)print(result)
示例解析
示例1:输入[[20,16],[15,11],[10,10],[9,10]]
- 排序处理:
- 原始书籍:
[20,16], [15,11], [10,10], [9,10]
- 按长度降序:
[20,16], [15,11], [10,10], [9,10]
(已有序) - 宽度序列:
[16, 11, 10, 10]
- 构建tail序列:
处理16: tail = [16]
处理11: 11<16 → tail = [16,11]
处理10: 10<11 → tail = [16,11,10]
处理10: 10>=10 → 替换第一个>=10的位置(tail[2]) → tail = [16,11,10]
- 结果:tail长度=3
示例2:输入[[10,20],[15,15],[16,10],[17,9]]
- 排序处理:
- 按长度降序:
[17,9], [16,10], [15,15], [10,20]
- 宽度序列:
[9,10,15,20]
- 构建tail序列:
处理9: tail = [9]
处理10: 10>=9 → 替换tail[0] → tail = [10]
处理15: 15>=10 → 替换tail[0] → tail = [15]
处理20: 20>=15 → 替换tail[0] → tail = [20]
- 结果:tail长度=1(只能选一本书)
示例3:输入[[5,4],[6,4],[6,7],[7,3]]
- 排序处理:
- 长度降序:
[7,3], [6,7], [6,4], [5,4]
- 长度相同时按宽度降序:
[7,3], [6,7], [6,4], [5,4]
- 宽度序列:
[3,7,4,4]
- 构建tail序列:
处理3: tail = [3]
处理7: 7>=3 → 替换tail[0] → tail = [7]
处理4: 4<7 → tail = [7,4]
处理4: 4>=4 → 替换第一个>=4的位置(tail[1]) → tail = [7,4]
- 结果:tail长度=2(如[6,7]和[5,4])
总结
关键要点
- 排序预处理:
- 按长度降序确保长度关系
- 长度相同时按宽度降序避免冲突
- 问题转化:
- 二维问题转化为一维序列问题
- 寻找最长严格递减子序列
- 二分优化:
- 维护最小末尾值序列
- 保证序列的严格递减性
应用场景
- 俄罗斯套娃问题
- 装箱问题
- 任务调度依赖
- 资源分配优化
- 游戏物体堆叠系统
该解法通过巧妙的排序策略将二维问题转化为一维问题,再利用二分查找优化经典动态规划算法,高效解决了书籍叠放问题。算法在保证正确性的同时具有良好的时间复杂度,适用于大规模数据处理。