Hard

题目描述

给你一个长度为 n 的下标从 0 开始的整数数组 nums,以及两个正整数 kdist

一个数组的 代价 是这个数组的 第一个 元素。比方说,[1,2,3] 的代价为 1[3,4,1] 的代价为 3

你需要将 nums 分割成 k连续且互不相交 的子数组,满足第二个子数组与第 k 个子数组中第一个元素的下标距离 不超过 dist。换句话说,如果你将 nums 分割成子数组 nums[0..(i1 - 1)], nums[i1..(i2 - 1)], …, nums[ik-1..(n - 1)],那么 ik-1 - i1 <= dist

请你返回这些子数组 代价之和最小值

示例 1:

输入:nums = [1,3,2,6,4,2], k = 3, dist = 3
输出:5
解释:将数组分割成 3 个子数组的最佳方案是:[1,3], [2,6,4], 和 [2]。这个方案是合法的,因为 ik-1 - i1 是 5 - 2 = 3,等于 dist。总代价为 nums[0] + nums[2] + nums[5],也就是 1 + 2 + 2 = 5。
可以证明没有别的方案可以得到更小的总代价 5。

示例 2:

输入:nums = [10,1,2,2,2,1], k = 4, dist = 3
输出:15
解释:将数组分割成 4 个子数组的最佳方案是:[10], [1], [2], 和 [2,2,1]。这个方案是合法的,因为 ik-1 - i1 是 3 - 1 = 2,小于 dist。总代价为 nums[0] + nums[1] + nums[2] + nums[3],也就是 10 + 1 + 2 + 2 = 15。
分割方案 [10], [1], [2,2,2], 和 [1] 不合法,因为 ik-1 和 i1 的距离是 5 - 1 = 4,大于 dist。
可以证明没有别的方案可以得到更小的总代价 15。

示例 3:

输入:nums = [10,8,18,9], k = 3, dist = 1
输出:36
解释:将数组分割成 3 个子数组的最佳方案是:[10], [8], 和 [18,9]。这个方案是合法的,因为 ik-1 - i1 是 2 - 1 = 1,等于 dist。总代价为 nums[0] + nums[1] + nums[2],也就是 10 + 8 + 18 = 36。
分割方案 [10], [8,18], 和 [9] 不合法,因为 ik-1 和 i1 的距离是 3 - 1 = 2,大于 dist。
可以证明没有别的方案可以得到更小的总代价 36。

提示:

  • 3 <= n <= 10^5
  • 1 <= nums[i] <= 10^9
  • 3 <= k <= n
  • k - 2 <= dist <= n - 2

解题思路

这道题的核心思想是枚举第二个子数组的起始位置,然后在满足距离限制的范围内找到最小的 k-2 个元素作为其余子数组的起始点。

解题思路:

  1. 枚举策略:第一个子数组必须从索引0开始,所以第一个代价是 nums[0]。我们枚举第二个子数组的起始位置 i(范围是 [1, n-k+1]),此时第二个子数组的代价是 nums[i]

  2. 距离限制:根据题意,第 k 个子数组的起始位置与第二个子数组起始位置的距离不能超过 dist,即最后一个子数组的起始位置最多在 i + dist

  3. 选择最小元素:在范围 [i+1, min(i+dist, n-1)] 中选择 k-2 个最小的元素作为剩余子数组的起始点。

  4. 双堆维护:使用两个堆来动态维护滑动窗口中的最小 k-2 个元素:

    • maxHeap:存储当前选中的 k-2 个最小元素(用最大堆,堆顶是这些元素中的最大值)
    • minHeap:存储未选中的元素(用最小堆,堆顶是这些元素中的最小值)
  5. 滑动窗口更新:当窗口右边界扩展时添加新元素,左边界收缩时移除旧元素,并动态平衡两个堆,确保 maxHeap 始终包含最小的 k-2 个元素。

这种方法能够在 O(n log n) 的时间复杂度内解决问题。

代码实现

class Solution {
public:
    long long minimumCost(vector<int>& nums, int k, int dist) {
        int n = nums.size();
        long long result = LLONG_MAX;
        
        // maxHeap stores the k-2 smallest elements (max at top)
        priority_queue<pair<int, int>> maxHeap;
        // minHeap stores the remaining elements (min at top)  
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> minHeap;
        
        multiset<pair<int, int>> maxSet, minSet;
        
        for (int i = 1; i <= n - k + 1; i++) {
            // Add new elements to the window
            int right = min(i + dist, n - 1);
            while (maxSet.size() + minSet.size() < right - i + 1) {
                int idx = i + maxSet.size() + minSet.size();
                if (maxSet.size() < k - 2) {
                    maxSet.insert({nums[idx], idx});
                } else {
                    minSet.insert({nums[idx], idx});
                }
            }
            
            // Remove elements that are out of window
            int left = i + 1;
            auto it = maxSet.lower_bound({0, left});
            while (it != maxSet.begin() && prev(it)->second < left) {
                it = maxSet.erase(prev(it));
            }
            
            it = minSet.lower_bound({0, left});
            while (it != minSet.begin() && prev(it)->second < left) {
                it = minSet.erase(prev(it));
            }
            
            // Balance the two sets
            while (maxSet.size() < k - 2 && !minSet.empty()) {
                auto minElem = *minSet.begin();
                minSet.erase(minSet.begin());
                maxSet.insert(minElem);
            }
            
            while (maxSet.size() > k - 2) {
                auto maxElem = *maxSet.rbegin();
                maxSet.erase(prev(maxSet.end()));
                minSet.insert(maxElem);
            }
            
            while (!maxSet.empty() && !minSet.empty() && 
                   maxSet.rbegin()->first > minSet.begin()->first) {
                auto maxElem = *maxSet.rbegin();
                auto minElem = *minSet.begin();
                maxSet.erase(prev(maxSet.end()));
                minSet.erase(minSet.begin());
                maxSet.insert(minElem);
                minSet.insert(maxElem);
            }
            
            if (maxSet.size() == k - 2) {
                long long cost = nums[0] + nums[i];
                for (auto& p : maxSet) {
                    cost += p.first;
                }
                result = min(result, cost);
            }
        }
        
        return result;
    }
};
class Solution:
    def minimumCost(self, nums: List[int], k: int, dist: int) -> int:
        import heapq
        from collections import defaultdict
        
        n = len(nums)
        result = float('inf')
        
        # Use lists to simulate heaps with lazy deletion
        max_heap = []  # negative values to simulate max heap
        min_heap = []
        max_heap_set = defaultdict(int)
        min_heap_set = defaultdict(int)
        
        def add_to_max_heap(val, idx):
            heapq.heappush(max_heap, (-val, -idx))
            max_heap_set[(val, idx)] += 1
            
        def add_to_min_heap(val, idx):
            heapq.heappush(min_heap, (val, idx))
            min_heap_set[(val, idx)] += 1
            
        def remove_from_max_heap(val, idx):
            max_heap_set[(val, idx)] -= 1
            if max_heap_set[(val, idx)] == 0:
                del max_heap_set[(val, idx)]
                
        def remove_from_min_heap(val, idx):
            min_heap_set[(val, idx)] -= 1
            if min_heap_set[(val, idx)] == 0:
                del min_heap_set[(val, idx)]
                
        def clean_max_heap():
            while max_heap and (-max_heap[0][0], -max_heap[0][1]) not in max_heap_set:
                heapq.heappop(max_heap)
                
        def clean_min_heap():
            while min_heap and (min_heap[0][0], min_heap[0][1]) not in min_heap_set:
                heapq.heappop(min_heap)
                
        def get_max_heap_size():
            return len(max_heap_set)
            
        def get_min_heap_size():
            return len(min_heap_set)
            
        for i in range(1, n - k + 2):
            # Expand window to the right
            right = min(i + dist, n - 1)
            
            # Add elements in range [i+1, right]
            for j in range(i + 1, right + 1):
                if get_max_heap_size() < k - 2:
                    add_to_max_heap(nums[j], j)
                else:
                    add_to_min_heap(nums[j], j)
            
            # Remove elements that are out of window (< i+1)
            to_remove = []
            for (val, idx) in max_heap_set:
                if idx < i + 1:
                    to_remove.append((val, idx))
            for val, idx in to_remove:
                remove_from_max_heap(val, idx)
                
            to_remove = []
            for (val, idx) in min_heap_set:
                if idx < i + 1:
                    to_remove.append((val, idx))
            for val, idx in to_remove:
                remove_from_min_heap(val, idx)
            
            # Balance heaps
            while get_max_heap_size() < k - 2 and get_min_heap_size() > 0:
                clean_min_heap()
                if min_heap:
                    val, idx = heapq.heappop(min_heap)
                    remove_from_min_heap(val, idx)
                    add_to_max_heap(val, idx)
            
            while get_max_heap_size() > k - 2:
                clean_max_heap()
                if max_heap:
                    neg_val, neg_idx = heapq.heappop(max_heap)
                    val, idx = -neg_val, -neg_idx
                    remove_from_max_heap(val, idx)
                    add_to_min_heap(val, idx)
            
            # Swap if needed
            while (get_max_heap_size() > 0 and get_min_heap_size() > 0):
                clean_max_heap()
                clean_min_heap()
                if not max_heap or not min_heap:
                    break
                    
                max_val = -max_heap[0][0]
                min_val = min_heap[0][0]
                
                if max_val > min_val:
                    # Remove from max heap
                    neg_val, neg_idx = heapq.heappop(max_heap)
                    val1, idx1 = -neg_val, -neg_idx
                    remove_from_max_heap(val1, idx1)
                    
                    # Remove from min heap
                    val2, idx2 = heapq.heappop(min_heap)
                    remove_from_min_heap(val2, idx2)
                    
                    # Swap
                    add_to_max_heap(val2, idx2)
                    add_to_min_heap(val1, idx1)
                else:
                    break
            
            if get_max_heap_size() == k - 2:
                cost = nums[0] + nums[i]
                for val, idx in max_heap_set:
                    cost += val * max_heap_set[(val, idx)]
                result = min(result, cost)
        
        return result
public class Solution {
    public long MinimumCost(int[] nums, int k, int dist) {
        int n = nums.Length;
        long result = long.MaxValue;
        
        var maxSet = new SortedSet<(int val, int idx)>();
        var minSet = new SortedSet<(int val, int idx)>();
        
        for (int i = 1; i <= n - k + 1; i++) {
            int right = Math.Min(i + dist, n - 1);
            
            // Add new elements to window
            for (int j = i + 1; j <= right; j++) {
                if (maxSet.Count < k - 2) {
                    maxSet.Add((nums[j], j));
                } else {
                    minSet.Add((nums[j], j));
                }
            }
            
            // Remove elements out of window
            var toRemoveFromMax = new List<(int, int)>();
            var toRemoveFromMin = new List<(int, int)>();
            
            foreach (var item in maxSet) {
                if (item.idx < i + 1) {
                    toRemoveFromMax.Add(item);
                }
            }
            
            foreach (var item in minSet) {
                if (item.idx < i + 1) {
                    toRemoveFromMin.Add(item);
                }
            }
            
            foreach (var item in toRemoveFromMax) {
                maxSet.Remove(item);
            }
            
            foreach (var item in toRemoveFromMin) {
                minSet.Remove(item);
            }
            
            // Balance sets
            while (maxSet.Count < k - 2 && minSet.Count > 0) {
                var minItem = minSet.Min;
                minSet.Remove(minItem);
                maxSet.Add(minItem);
            }
            
            while (maxSet.Count > k - 2) {
                var maxItem = maxSet.Max;
                maxSet.Remove(maxItem);
                minSet.Add(maxItem);
            }
            
            while (maxSet.Count > 0 && minSet.Count > 0 && 
                   maxSet.Max.val > minSet.Min.val) {
                var maxItem = maxSet.Max;
                var minItem = minSet.Min;
                maxSet.Remove(maxItem);
                minSet.Remove(minItem);
                maxSet.Add(minItem);
                minSet.Add(maxItem);
            }
            
            if (maxSet.Count == k - 2) {
                long cost = nums[0] + nums[i];
                foreach (var item in maxSet) {
                    cost += item.val;
                }
                result = Math.Min(result, cost);
            }
        }
        
        return result;
    }
}
var minimumCost = function(nums, k, dist) {
    const n = nums.length;
    let result = Number.MAX_SAFE_INTEGER;
    
    // Use arrays to simulate sorted sets
    let maxSet = [];
    let minSet = [];
    
    const addToMaxSet = (val, idx) => {
        maxSet.push([val, idx]);
        maxSet.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
    };
    
    const addToMinSet = (val, idx) => {
        minSet.push([val, idx]);
        minSet.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
    };
    
    const removeFromMaxSet = (val, idx) => {
        const index = maxSet.findIndex(item => item[0]

复杂度分析

项目复杂度
时间复杂度O(n log n)
空间复杂度O(n)

解释:

  • 时间复杂度:外层循环遍历 O(n) 个起始位置,每次操作涉及有序集合的插入、删除和查找操作,每个操作的时间复杂度为 O(log n),总体时间复杂度为 O(n log n)
  • 空间复杂度:需要维护两个有序集合存储元素,在最坏情况下需要存储 O(n) 个元素

相关题目