leetcode 3013. 将数组分成最小总代价的子数组 II 困难

给你一个下标从 0 开始长度为 n 的整数数组 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 是分割成 3 个子数组的最小总代价。

示例 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 是分割成 4 个子数组的最小总代价。

示例 3:

复制代码
输入:nums = [10,8,18,9], k = 3, dist = 1
输出:36
解释:将数组分割成 4 个子数组的最优方案是:[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 个子数组的最小总代价。

提示:

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

分析:第一个子数组的首位一定是 nums[0],那么之后要从 nums[1] 开始划分 k-1 个子数组,且第二个子数组和最后一个子数组的首位数字下标之差要小于等于 dist,要求的总代价就是 nums[0] 与之后 [1+x,1+dist+x](其中 x 为 0 到 n-1-dist 之间的值) 这个区间内前 k-1 个最小值之和。

可以维护一个大小为 dist+1 的滑动窗口,记录其中的前 k 个数字作为当前区间的划分代价。为了达到这个目的,需要用两个有序集合,一个记录前 k 个较小值,一个记录剩下的 dist+1-k 个数。始终维护这两个有序集合里一个有 k 个较小值,两个集合的值为 1+dist 即可。实现方法可以用mutiset,也可以用一个最大堆加一个最小堆。用堆需要注意删除的时机。

cpp 复制代码
class Solution {
public:
    long long minimumCost(vector<int>& nums, int k, int dist) {
        long long ans=0x3fffffff,sum=0;
        int n=nums.size(),cnt_first=0;k--;
        
        priority_queue<int,vector<int>,less<int>>first;
        priority_queue<int,vector<int>,greater<int>>origin,second;
        for(int i=1,j=0;j<=dist;++j,++i)
            origin.push(nums[i]);
        for(int i=0,j=0;i<=dist;++i,++j)
        {
            int x=origin.top();origin.pop();
            if(j<k)first.push(x),sum+=x;
            else second.push(x);
        }
        ans=sum;cnt_first=first.size();

        map<int,int>wait_del;
        for(int i=2;i<n-dist;++i)
        {
            int del=nums[i-1],add=nums[i+dist];

            wait_del[del]++;
            if(del<=first.top())sum-=del,cnt_first--;

            while(!first.empty()&&wait_del[first.top()])
                wait_del[first.top()]--,first.pop();
            while(!second.empty())
            {
                if(wait_del[second.top()])wait_del[second.top()]--,second.pop();
                else if(cnt_first<k)sum+=second.top(),first.push(second.top()),second.pop(),cnt_first++;
                else break;
            }

            if(add<=first.top())
            {
                first.push(add),sum+=add,cnt_first++;
                if(cnt_first>k)
                {
                    while(!first.empty())
                    {
                        if(wait_del[first.top()])wait_del[first.top()]--,first.pop();
                        else if(cnt_first>k)sum-=first.top(),second.push(first.top()),first.pop(),cnt_first--;
                        else break;
                    } 
                }
                else if(cnt_first<k)
                {
                    while(!second.empty())
                    {
                        if(wait_del[second.top()])wait_del[second.top()]--,second.pop();
                        else if(cnt_first<k)sum+=second.top(),first.push(second.top()),second.pop(),cnt_first++;
                        else break;
                    }
                }
            }
            else
            {
                second.push(add);
                if(cnt_first>k)
                {
                    while(!first.empty())
                    {
                        if(wait_del[first.top()])wait_del[first.top()]--,first.pop();
                        else if(cnt_first>k)sum-=first.top(),second.push(first.top()),first.pop(),cnt_first--;
                        else break;
                    } 
                }
                else if(cnt_first<k)
                {
                    while(!second.empty())
                    {
                        if(wait_del[second.top()])second.pop(),wait_del[second.top()]--;
                        else if(cnt_first<k)sum+=second.top(),first.push(second.top()),second.pop(),cnt_first++;
                        else break;
                    }
                }
            }
           
            ans=min(ans,sum);
        }

        return ans+nums[0];
    }
};
相关推荐
iAkuya3 小时前
(leetcode)力扣100 59括号生成(回溯||按括号序列的长度递归)
算法·leetcode·职场和发展
共享家95273 小时前
双指针算法(一)
数据结构·算法·leetcode
渐暖°3 小时前
【leetcode算法从入门到精通】9. 回文数
算法·leetcode·职场和发展
我是咸鱼不闲呀3 小时前
力扣Hot100系列18(Java)——[技巧]总结 (只出现一次的数字,多数元素,颜色分类,下一个排列,寻找重复数)
java·算法·leetcode
鱼跃鹰飞3 小时前
Leetcode279:完全平方数
数据结构·算法·leetcode·面试
芒克芒克3 小时前
LeetCode 跳跃游戏 II 最优解法:贪心算法
leetcode·游戏·贪心算法
User_芊芊君子3 小时前
【LeetCode原地复写零】:双指针+逆向填充,O(n)时间O(1)空间最优解!
android·linux·leetcode
进击的荆棘4 小时前
优选算法——滑动窗口
c++·算法·leetcode
多米Domi0115 小时前
0x3f 第49天 面向实习的八股背诵第六天 过了一遍JVM的知识点,看了相关视频讲解JVM内存,垃圾清理,买了plus,稍微看了点确定一下方向
jvm·数据结构·python·算法·leetcode