第 443 场周赛——使 K 个子数组内元素相等的最少操作数
题目
题解
这题就是大杂烩,使用了延迟堆来求出每个窗口内所有值变成它中位数的操作次数存储在dis中,dis[i]的i是它的左端点,最后再用划分型dp求出最小操作数。
我们先一个一个来分析,如何求取一块区间内的中位数,我们可以用对顶堆实现,可以参考题目295. 数据流的中位数。
这是不对元素进行删除时中位数的求法。
那如果我们要用一个窗口不断往右走,那该如何求这个窗口内的中位数呢?
由于我们每次移动后前面的值出了窗口,我们如果每次遍历堆找出那个值时很麻烦的,这时候我们想到了延迟堆的做法,删除的时候,我们只记录「要删除一个值为 x 的数」,并不去「执行」删除操作。等到要出堆(或者查看堆顶)时才真正地执行删除操作。
那我们如何求的这个窗口内将所有值变成一样的操作数呢?
我们可以参考题目2602. 使数组元素全部相等的最少操作次数,题解用的是排序+前缀和+二分查找,我们可以用面积来求的需要的操作数。
那我们只需要在延迟堆的代码中再开一个变量维护和大小即可。
最后就是我们该如何使用dp完成呢?
将数组分成(恰好/至多)k 个连续子数组,计算与这些子数组有关的最优值。一般定义 f[i][j] 表示将长为 j 的前缀 a[:j] 分成 i 个连续子数组所得到的最优解。枚举最后一个子数组的左端点 L,从 f[i−1][L] 转移到 f[i][j],并考虑 a[L:j] 对最优解的影响。
本题f[i][j]表示前 j 个元素(即 nums[0..j-1])分成 i 个长度为 x 的子数组时,最小的操作数。只需要初始化f[i][i*x−1]=∞。并不需要初始化 j<i⋅x−1 的状态,因为我们不会访问这些状态。
参考代码
template<typename T, typename Compare = less<T>>
class LazyHeap {
    priority_queue<T, vector<T>, Compare> pq;
    unordered_map<T, int> remove_cnt;
    size_t sz = 0;
    long long s = 0;
    void apply_remove() {
        while(!pq.empty() && remove_cnt[pq.top()] > 0) {
            remove_cnt[pq.top()]--;
            pq.pop();
        }
    }
public:
    size_t size(){return sz;}
    long long sum() {
        return s;
    }
    void remove(T x) {
        remove_cnt[x]++;
        sz--;
        s -= x;
    }
    T top() {
        apply_remove();
        return pq.top();
    }
    T pop() {
        apply_remove();
        T x = pq.top();
        pq.pop();
        sz--;
        s -= x;
        return x;
    }
    void push(T x) {
        if(remove_cnt[x] > 0) remove_cnt[x]--;
        else pq.push(x);
        sz++;
        s += x;
    }
    T push_pop(T x) {
        apply_remove();
        pq.push(x);
        s += x;
        x = pq.top();
        pq.pop();
        s -= x;
        return x;
    }
};
class Solution {
    vector<long long> medianSlidingWindow(vector<int>& nums, int k) {
        int n = nums.size();
        vector<long long> ans(n - k + 1);
        LazyHeap<int> left;
        LazyHeap<int, greater<int>> right; 
        for (int i = 0; i < n; i++) {
            int in = nums[i];
            if (left.size() == right.size()) {
                left.push(right.push_pop(in));
            } else {
                right.push(left.push_pop(in));
            }
            int l = i + 1 - k;
            if (l < 0) continue;
            long long v = left.top();
            long long s1 = v * left.size() - left.sum();
            long long s2 = right.sum() - v * right.size();
            ans[l] = s1 + s2;
            int out = nums[l];
            if (out <= left.top()) {
                left.remove(out);
                if (left.size() < right.size()) {
                    left.push(right.pop()); 
                }
            } else {
                right.remove(out);
                if (left.size() > right.size() + 1) {
                    right.push(left.pop()); 
                }
            }
        }
        return ans;
    } 
public:
    long long minOperations(vector<int>& nums, int x, int k) {
        int n = nums.size();
        vector<long long> dis = medianSlidingWindow(nums, x);
        vector f(k + 1, vector<long long>(n + 1));
        for(int i = 1;i <= k; i++) {
            f[i][i * x - 1] = LLONG_MAX;
            for(int j = i * x; j <= n - (k - i) * x; j++) {
                f[i][j] = min(f[i][j - 1], f[i - 1][j - x] + dis[j - x]);
            }
        }
        return f[k][n];
    }
};

                
            
        
浙公网安备 33010602011771号