小m的书柜设计

97 阅读8分钟

问题描述

小M终于拥有了属于自己的房子,并打算好好设计书房。他有很多珍藏的书籍,这些书按照时间顺序保存着。现在,他希望在书柜的每个格子里,书籍的高度差异不超过一个特定值 k,并且书籍的排列要满足时间上的连续性(即书籍在格子内的序号要连续)。同时,书柜的每个格子能容纳的书越多越好。

你的任务是帮助小M计算出在同一个格子里最多能放多少本书,以及有多少种放置方式可以达到这个要求,并且列出每种放置方式中书籍的最小和最大序号。

返回的列表中,第一行的第一个元素是最大的书的数量,第二个元素是放置方式数量m,此后m个列表分别表示书籍的最小和最大序号。

分析

1. 理解问题目标

我们的主要目标是在满足两个条件的基础上,确定在书柜的同一个格子里能放置的最多书籍数量,以及达到这个最多数量的放置方式有多少种,并列出每种放置方式中书籍的最小和最大序号。

2. 明确条件限制

  • 高度差异限制:书柜每个格子里书籍的高度差异不能超过特定值 。这意味着我们在选择放入同一个格子的书籍时,需要考虑它们高度之间的差值不能超出这个规定值。
  • 时间连续性限制:书籍在格子内的序号要连续。也就是说,我们挑选的书籍在按照时间顺序排列的序列中,必须是相邻的,不能有间断。

3. 解决思路分析

确定最大书籍数量

  • 我们可以从第一本书开始,依次尝试向后扩展选取连续的书籍,同时检查这些书籍的高度差异是否满足不超过  的条件。
  • 具体做法是,设置两个指针,一个指针  指向当前考虑的连续书籍序列的起始位置(初始为第一本书),另一个指针  则逐步向后移动,尝试扩展这个序列。每次移动  指针后,检查从  到  这些书籍的高度差异是否在允许范围内。如果满足条件,就继续扩展;一旦不满足,就停止扩展当前序列,记录下这个序列的长度(即 ),并与之前找到的最长序列长度进行比较,更新最长序列长度。
  • 重复这个过程,让  指针也逐步向后移动,遍历所有可能的连续书籍序列,最终就能确定在满足高度差异限制和时间连续性限制下,同一个格子里能放置的最多书籍数量。

确定放置方式数量及具体放置方式

  • 一旦找到了最大书籍数量 ,我们需要再次遍历所有可能的连续书籍序列,找出长度恰好等于  的那些序列。每找到一个这样的序列,就意味着找到了一种满足要求的放置方式。

  • 在遍历过程中,可以通过记录每个满足  长度的序列的起始序号(即最小序号)和结束序号(即最大序号)来确定具体的放置方式。同时,用一个计数器记录找到的满足  长度的序列的数量,这个数量就是放置方式的数量。

通过以上的分析步骤,我们就能够按照要求计算出在同一个格子里最多能放多少本书,以及有多少种放置方式可以达到这个要求,并列出每种放置方式中书籍的最小和最大序号。

代码

def solution(n, k, array):  
    max_count = 0  
    configurations = []  
    
    left = 0  
    
    for right in range(n):  
        # Expand the right pointer and check the condition  
        while left < right and (max(array[left:right+1]) - min(array[left:right+1]) > k):  
            left += 1  
        
        # Calculate the number of books in the current valid window  
        current_count = right - left + 1  
        
        if current_count > max_count:  
            max_count = current_count  
            configurations = [[left + 1, right + 1]]  # Store 1-based index  
        elif current_count == max_count:  
            configurations.append([left + 1, right + 1])  # Store 1-based index  
    
    return [[max_count, len(configurations)]] + configurations  

# Test cases  
if __name__ == "__main__":  
    print(solution(3, 3, [14, 12, 10]) == [[2, 2], [1, 2], [2, 3]])  
    print(solution(2, 0, [10, 10]) == [[2, 1], [1, 2]])  
    print(solution(4, 5, [8, 19, 10, 13]) == [[2, 1], [3, 4]])

2. 代码逻辑分析

变量初始化

  • max_count = 0:用于记录在满足条件下,同一个格子里能放置的最多书籍数量,初始化为 0。
  • configurations = []:这是一个列表,用于存储满足最多书籍数量条件的每种放置方式的书籍最小和最大序号(以 1 为起始索引),初始为空列表。
  • left = 0:定义了一个指针 left,用于标记当前考虑的连续书籍序列的起始位置,初始指向数组的第一个元素。

遍历书籍数组

  • 代码通过一个 for 循环 for right in range(n): 来遍历书籍数组 array,其中 right 指针从 0 开始逐步增加,直到遍历完整个数组。这个 right 指针用于扩展当前考虑的连续书籍序列的末尾位置。

检查并调整序列

  • 在每次 right 指针移动后,会进入一个 while 循环 while left < right and (max(array[left:right+1]) - min(array[left:right+1]) > k):。这个循环的目的是在当前 right 指针位置下,检查从 left 到 right 所构成的连续书籍序列的高度差异是否超过了允许值 k。如果超过了,就通过不断将 left 指针向右移动(left += 1)来调整序列,直到满足高度差异条件或者 left 与 right 相等。

计算当前有效序列的书籍数量

  • 在 while 循环结束后,意味着当前从 left 到 right 的连续书籍序列满足了高度差异条件。此时通过 current_count = right - left + 1 计算出这个当前有效窗口(即满足条件的连续书籍序列)内的书籍数量。

更新最多书籍数量及放置方式

  • 如果计算出的 current_count 大于之前记录的 max_count,说明找到了一个更长的满足条件的连续书籍序列。此时更新 max_count 为新的更长序列的书籍数量,并将当前序列的最小和最大序号(以 1 为起始索引,通过 [left + 1, right + 1] 计算)作为唯一的放置方式存储到 configurations 列表中。
  • 如果 current_count 等于 max_count,说明找到了另一种与当前最长序列长度相同的满足条件的放置方式。此时将该放置方式的最小和最大序号(同样以 1 为起始索引)添加到 configurations 列表中。

滑动窗口算法原理

滑动窗口算法是一种用于处理数组或序列数据的优化技巧,它通过维护一个窗口(通常是一个连续的子序列)在数据结构上滑动,来高效地解决一些特定类型的问题。在滑动过程中,根据窗口内数据满足的条件动态调整窗口的大小,从而在一次遍历中找到满足特定要求的子序列或计算相关指标。

在给定代码中的应用

窗口定义与初始化

在代码中,通过两个指针 left 和 right 定义了一个滑动窗口,这个窗口代表了当前考虑的连续书籍序列。初始时,left 被设置为 0,right 会随着循环逐步遍历数组 array,从而使窗口从数组的起始位置开始,逐步向右 “滑动” 扩展。

窗口滑动与条件判断

  • 扩展窗口:在每次循环中,right 指针会向右移动一位,以此来尝试扩展窗口,将更多的书籍纳入当前考虑的连续书籍序列范围内。
  • 条件判断与窗口调整:当窗口扩展后,会通过 while left < right and (max(array[left:right+1]) - min(array[left:right+1]) > k): 这个条件判断来检查当前窗口内书籍高度的差异是否超过了允许值 k。如果超过了,就说明当前窗口不符合要求,需要通过不断将 left 指针向右移动(left += 1)来缩小窗口,直到窗口内书籍高度差异满足不超过 k 的条件为止。这个过程就是根据条件动态调整窗口大小的过程,类似于滑动窗口根据特定规则在数组上滑动并适配。

计算与更新基于窗口的结果

  • 在每次窗口调整到满足条件后,会通过 current_count = right - left + 1 计算当前窗口内(即满足条件的连续书籍序列)的书籍数量。然后,将这个数量与之前记录的最多书籍数量 max_count 进行比较:

    • 如果 current_count 大于 max_count,说明找到了一个更长的满足条件的连续书籍序列,此时会更新 max_count 以及存储放置方式的 configurations 列表,记录下新的最长序列对应的放置方式(书籍的最小和最大序号)。

    • 如果 current_count 等于 max_count,则找到了另一种与当前最长序列长度相同的满足条件的放置方式,会将其添加到 configurations 列表中。

通过这样不断地滑动窗口、判断条件、调整窗口大小以及更新结果,最终能够在一次遍历数组的过程中,找出在满足书籍高度差异不超过 k 且书籍序号连续的条件下,同一个格子里能放置的最多书籍数量以及达到该数量的所有放置方式。所以,整体代码利用滑动窗口算法高效地解决了题目所设定的关于书籍放置的优化问题。