查看: 61|回复: 0

牛客17799题:滑动窗口+优先队列解决多数组最小区间

[复制链接]
  • TA的每日心情
    擦汗
    2025-8-15 15:18
  • 签到天数: 35 天

    [LV.5]常住居民I

    34

    主题

    0

    回帖

    65

    积分

    注册会员

    Rank: 2

    积分
    65
    发表于 2025-8-7 10:04:44 | 显示全部楼层 |阅读模式
    一、题目解读
    牛客17799题要求在一个由K个有序数组组成的集合中,找到包含所有数组元素的最小区间范围。即需确定一个区间[range_start, range_end],使得每个数组中至少有一个元素落在该区间内,且区间长度最小。题目考验对多数组合并与区间优化的处理能力。
    二、解题思路
    采用“滑动窗口+优先队列”策略:
    1. 初始化:将每个数组的首元素加入小根堆(优先队列),记录当前堆顶元素(最小值)与最大值。
    2. 滑动窗口:每次取出堆顶元素(当前窗口最小值),更新区间范围。若该元素所在数组存在后续元素,则将其加入堆并更新最大值。
    3. 循环终止条件:当某个数组的所有元素已被遍历时结束,确保每个数组至少贡献一个元素到区间。
    4. 区间更新逻辑:通过比较新旧区间的长度和起始值,动态维护最小范围。
    三、解题步骤
    1. 创建包含元素值、行索引、列索引的结构体,重载运算符支持优先队列排序
    2. 初始化堆:遍历K个数组首元素,加入堆并记录当前最大值。
    3. 循环:
    ○ 弹出堆顶元素,更新区间范围(根据差值或起始值优先级)。
    ○ 若该元素非所在数组末尾,则将其下一元素加入堆并更新最大值。
    ○ 若某数组遍历完毕,终止循环。
    4. 返回最终区间[range_start, range_end]。
    四、代码与注释
    1. #include <iostream>
    2. #include <vector>
    3. #include <queue>
    4. #include <climits>
    5. using namespace std;

    6. struct Element {
    7.     int val;    // 元素值
    8.     int row;    // 所属数组索引
    9.     int col;    // 在数组中的位置
    10.    
    11.     bool operator>(const Element& other) const {
    12.         return val > other.val;
    13.     }
    14. };

    15. vector<int> smallestRange(vector<vector<int>>& nums) {
    16.     int k = nums.size();
    17.     priority_queue<Element, vector<Element>, greater<Element>> min_heap;
    18.    
    19.     int current_max = INT_MIN;
    20.     // 初始化堆,放入每个数组的第一个元素
    21.     for (int i = 0; i < k; ++i) {
    22.         min_heap.push({nums[i][0], i, 0});
    23.         current_max = max(current_max, nums[i][0]);
    24.     }
    25.    
    26.     int range_start = 0, range_end = INT_MAX;
    27.    
    28.     while (true) {
    29.         Element min_element = min_heap.top();
    30.         min_heap.pop();
    31.         
    32.         // 更新最小区间
    33.         if (current_max - min_element.val < range_end - range_start) {
    34.             range_start = min_element.val;
    35.             range_end = current_max;
    36.         } else if (current_max - min_element.val == range_end - range_start &&
    37.                   min_element.val < range_start) {
    38.             range_start = min_element.val;
    39.             range_end = current_max;
    40.         }
    41.         
    42.         // 如果某个数组已经遍历完,结束循环
    43.         if (min_element.col + 1 == nums[min_element.row].size()) {
    44.             break;
    45.         }
    46.         
    47.         // 将当前数组的下一个元素加入堆
    48.         int next_val = nums[min_element.row][min_element.col + 1];
    49.         min_heap.push({next_val, min_element.row, min_element.col + 1});
    50.         current_max = max(current_max, next_val);
    51.     }
    52.    
    53.     return {range_start, range_end};
    54. }

    55. int main() {
    56.     int k, n;
    57.     cin >> k >> n;
    58.    
    59.     vector<vector<int>> nums(k, vector<int>(n));
    60.     for (int i = 0; i < k; ++i) {
    61.         for (int j = 0; j < n; ++j) {
    62.             cin >> nums[i][j];
    63.         }
    64.     }
    65.    
    66.     vector<int> result = smallestRange(nums);
    67.     cout << result[0] << " " << result[1] << endl;
    68.    
    69.     return 0;
    70. }
    复制代码


    五、总结
    本解法核心在于利用优先队列维护多数组元素的有序性,通过滑动窗口动态调整区间范围,避免了暴力遍历所有组合。时间复杂度为O(NlogK)(N为总元素数,K为数组数量),空间复杂度为O(K)。通过优化区间更新逻辑,确保在相等长度时优先选择更小区间,从而准确求解最小范围。

    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    快速回复 返回顶部 返回列表