美文网首页
【教3妹学编程-算法题】将数组分成最小总代价的子数组 II

【教3妹学编程-算法题】将数组分成最小总代价的子数组 II

作者: 程序员小2 | 来源:发表于2024-02-15 09:59 被阅读0次
    瑟瑟发抖

    2哥 : 叮铃铃,3妹,过年干嘛呢,是不是逛吃逛吃,有没有长胖呢。
    3妹:切,我妈张罗着要给我相亲呢。
    2哥 : 相亲?哈哈哈哈
    3妹:别笑了,我妈说跟我年龄相等的人都已经孩子上小学了,跟她年龄相等的人孙子最少都会打酱油了。
    2哥 :哈哈哈哈,让我先笑一会儿
    3妹:话说2哥过年在家里也刷题吗?
    2哥:当然了,雷打不动。
    3妹:好吧,还得是2哥🐂,我有几天懈怠了。
    2哥:好吧,说到刷题啊,今天有一道“最小”的题目, 让我们先做一下吧~

    吃瓜

    题目:

    给你一个下标从 0 开始长度为 n 的整数数组 nums 和两个 正 整数 k 和 dist 。

    一个数组的 代价 是数组中的 第一个 元素。比方说,[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

    思路:

    思考

    维护两个有序集合前K-1小的数,
    第一段的第一个数是确定的,即 nums[0]。

    如果知道了第二段的第一个数的位置(记作 p),第三段的第一个数的位置,……,第 k 段的第一个数的位置(记作 q),那么这个划分方案也就确定了。

    考虑到 q−p≤dist,本题相当于在一个大小固定为 dist+1的滑动窗口内,求前 k−1 小的元素和。

    java代码:

    public class Solution {
        public long minimumCost(int[] nums, int k, int dist) {
            k--;
            sumL = nums[0];
            for (int i = 1; i < dist + 2; i++) {
                sumL += nums[i];
                L.merge(nums[i], 1, Integer::sum);
            }
            sizeL = dist + 1;
            while (sizeL > k) {
                l2r();
            }
    
            long ans = sumL;
            for (int i = dist + 2; i < nums.length; i++) {
                // 移除 out
                int out = nums[i - dist - 1];
                if (L.containsKey(out)) {
                    sumL -= out;
                    sizeL--;
                    removeOne(L, out);
                } else {
                    removeOne(R, out);
                }
    
                // 添加 in
                int in = nums[i];
                if (in < L.lastKey()) {
                    sumL += in;
                    sizeL++;
                    L.merge(in, 1, Integer::sum);
                } else {
                    R.merge(in, 1, Integer::sum);
                }
    
                // 维护大小
                if (sizeL == k - 1) {
                    r2l();
                } else if (sizeL == k + 1) {
                    l2r();
                }
    
                ans = Math.min(ans, sumL);
            }
            return ans;
        }
    
        private long sumL;
        private int sizeL;
        private final TreeMap<Integer, Integer> L = new TreeMap<>();
        private final TreeMap<Integer, Integer> R = new TreeMap<>();
    
        private void l2r() {
            int x = L.lastKey();
            removeOne(L, x);
            sumL -= x;
            sizeL--;
            R.merge(x, 1, Integer::sum);
        }
    
        private void r2l() {
            int x = R.firstKey();
            removeOne(R, x);
            sumL += x;
            sizeL++;
            L.merge(x, 1, Integer::sum);
        }
    
        private void removeOne(Map<Integer, Integer> m, int x) {
            int cnt = m.get(x);
            if (cnt > 1) {
                m.put(x, cnt - 1);
            } else {
                m.remove(x);
            }
        }
    }
    
    

    相关文章

      网友评论

          本文标题:【教3妹学编程-算法题】将数组分成最小总代价的子数组 II

          本文链接:https://www.haomeiwen.com/subject/bmbeadtx.html