跳至主要內容

力扣第 122 场双周赛

大约 2 分钟

力扣第 122 场双周赛

比赛时间 2024-01-20。本场周赛国服共 150 人 AK。

T1. 将数组分成最小总代价的子数组 I(3 分)

解题思路

贪心。nums[1:] 部分取最小的两个数,加上 nums[0]

时间复杂度:O(nlogn)

参考代码

class Solution {
    public int minimumCost(int[] nums) {
        int n = nums.length;
        Arrays.sort(nums, 1, n);
        return nums[0] + nums[1] + nums[2];
    }
}

T2. 判断一个数组是否可以变为有序(4 分)

解题思路

分组循环。排序模拟。再判断整个数组是否有序即可。

时间复杂度:O(nlogn)

参考代码

class Solution {
    public boolean canSortArray(int[] nums) {
        int n = nums.length;
        int i = 0;
        while (i < n) {
            int st = i;
            for (i++; i < n && Integer.bitCount(nums[i]) == Integer.bitCount(nums[i - 1]); i++) {
            }
            Arrays.sort(nums, st, i);
        }

        for (int j = 1; j < n; j++) {
            if (nums[j - 1] > nums[j]) return false;
        }
        return true;
    }
}

T3. 通过操作使数组长度最小(5 分)

解题思路

脑筋急转弯。

假设 a < b,那么 a % b = a,意味着小数可以无损消除大数。直接计算最小数的频次 c,返回 (c + 1) / 2,不出意外 wa 了。

注意到可能由 b % a 得到比 a 小的非零数,这种情况,最小数频次变为 1,答案为 1

时间复杂度:O(n)

参考代码

class Solution {
    public int minimumArrayLength(int[] nums) {
        int mn = Arrays.stream(nums).min().orElseThrow();
        int c = 0;
        for (int v : nums) {
            if (v == mn) c++;
            if (v % mn > 0) {
                return 1;
            }
        }
        return (c + 1) / 2;
    }
}

T4. 将数组分成最小总代价的子数组 II(7 分)

解题思路

滑动窗口 + 双平衡树模拟 multiset。注意优先队列删除指定元素时间复杂度是 O(n),除非用懒删除堆,不过比较复杂。

时间复杂度:O(nlogn)

参考代码

class Solution {
    public long minimumCost(int[] nums, int k, int dist) {
        int n = nums.length;
        // 窗口大小 m,前 k-1 小的和,等价于 总和减去前 m-(k-1) 大的和
        int m = dist + 1;
        MultiSets multiSets = new MultiSets(m, m - (k - 1));
        for (int i = 1; i < m + 1; i++) {
            multiSets.add(nums[i]);
        }
        long ans = multiSets.tot - multiSets.sumX;
        for (int i = m + 1; i < n; i++) {
            multiSets.add(nums[i]);
            multiSets.del(nums[i - m]);

            ans = Math.min(ans, multiSets.tot - multiSets.sumX);
        }
        return ans + nums[0];
    }

    private static class MultiSets {
        int n, k;
        TreeMap<Integer, Integer> xMap, yMap;
        int xsz, ysz;
        long sumX, tot;

        // n:窗口大小, k:第 k 大
        public MultiSets(int n, int k) {
            this.n = n;
            this.k = k;
            this.xMap = new TreeMap<>();
            this.yMap = new TreeMap<>();
            this.xsz = 0;
            this.ysz = 0;
            sumX = 0;
        }

        private void add(int v) {
            yInsertV(v);
            balance();
            tot += v;
        }

        private void del(int v) {
            if (xMap.containsKey(v)) {
                xEraseV(v);
            } else {
                yEraseV(v);
            }
            balance();
            tot -= v;
        }

        private void balance() {
            if (xsz + ysz < n) return;
            while (xsz < k) {
                int iy = yMap.lastKey();
                yEraseV(iy);
                xInsertV(iy);
            }
            if (xsz == 0 || ysz == 0) return;
            while (true) {
                int ix = xMap.firstKey();
                int iy = yMap.lastKey();
                if (ix >= iy) break;
                xEraseV(ix);
                yEraseV(iy);
                xInsertV(iy);
                yInsertV(ix);
            }
        }

        private void xInsertV(int v) {
            xMap.put(v, xMap.getOrDefault(v, 0) + 1);
            xsz++;
            sumX += v;
        }

        private void yInsertV(int v) {
            yMap.put(v, yMap.getOrDefault(v, 0) + 1);
            ysz++;
        }

        private void xEraseV(int v) {
            xMap.put(v, xMap.getOrDefault(v, 0) - 1);
            if (xMap.get(v) == 0) xMap.remove(v);
            xsz--;
            sumX -= v;
        }

        private void yEraseV(int v) {
            yMap.put(v, yMap.getOrDefault(v, 0) - 1);
            if (yMap.get(v) == 0) yMap.remove(v);
            ysz--;
        }
    }
}

(全文完)