标签:需要 使用 题解 owb pre ref ace 最小 turn
? LeetCode第217周赛的第三题,比赛时卡了一个小时,没有想到O(n)的做法。对差分不熟悉,但是最关键的还是扫描的思路没有想到。由于这道题有这么几个点比较重要,觉得应该特别记录一下。
//
// Created by root on 2020/11/30.
//
#include <vector>
using namespace std;
class Solution {
public:
    // 差分数组:b[k]。
    // 差分数组更新,对[l, r]区间++:b[l]++, b[r + 1]--;
    // 和为k时最小操作次数即b[0] + b[1] + ... + b[k]
    vector<int> b;
    void update(int l, int r, int x) {
        b[l] += x;
        b[r + 1] -= x;
    }
    int minMoves(vector<int>& nums, int limit) {
        b.resize(limit * 2 + 2);
        int n = nums.size();
        for (int i = 0; i < n / 2; i++) {
            int lo = min(nums[i], nums[n - i - 1]) + 1;  // 单步操作可以达到的最小值就是两者之min + 1
            int hi = max(nums[i], nums[n - i - 1]) + limit;  // 最大值就是两者之max + limit
            int sum = nums[i] + nums[n - i - 1];  // 当前和
            // 想象一个数轴,K在移动:
            // lo <= K <= hi时,操作次数为1
            // K = sum时,操作次数为0
            // 其余情况,操作次数为2,为方便起见,我们设初始步数就为2,这样对其他操作我们只需要--
            update(lo, hi, -1);  // 对[lo, hi]中操作次数--
            update(sum, sum, -1);  // 对[sum, sum]中操作次数继续--
        }
        // 最大操作步数为 n / 2 * 2
        int now = n;
        int res = n;
        // 数组互补时,选中的和K的范围[2, limit * 2 + 2]
        for (int i = 2; i < limit * 2 + 2; i++) {
            now += b[i];
            res = min(res, now);
        }
        return res;
    }
};
附上树状数组解法:
// 树状数组解法:区间更新、单点查询
class BIT {
public:
    vector<int> trees;
    int max_n;
    BIT(int n) {
        trees.resize(n);
        max_n = n;
    }
    int lowBit(int x) {
        return x & (-x);
    }
    void update(int pos, int x) {
        for (int i = pos; i < max_n; i += lowBit(i)) {
            trees[i] += x;
        }
    }
    int query(int pos) {
        int res = 0;
        for (int i = pos; i; i -= lowBit(i)) {
            res += trees[i];
        }
        return res;
    }
    // 引入差分,使树状数组可以进行区间更新
    void update(int l, int r, int x) {
        update(l, x);
        update(r + 1, -x);
    }
};
class Solution2 {
public:
    int minMoves(vector<int>& nums, int limit) {
        BIT bit(limit * 2 + 2);
        int n = nums.size();
        for (int i = 0; i < n / 2; i++) {
            int lo = min(nums[i], nums[n - i - 1]) + 1;
            int hi = max(nums[i], nums[n - i - 1]) + limit;
            int sum = nums[i] + nums[n - i - 1];
            bit.update(lo, hi, -1);
            bit.update(sum, sum, -1);
        }
        // 由于树状数组的区间查询不需要遍历,因此这里now直接记录K == i时,减去的操作数。因此,答案需要返回n + res
        int now = 0;
        int res = n;
        // 数组互补时,选中的和K的范围[2, limit * 2 + 2]
        for (int i = 2; i < limit * 2 + 2; i++) {
            now = bit.query(i);
            res = min(res, now);
        }
        return n + res;
    }
};
这道题让我对树状数组的使用理解又加深了一些,后面可能会总结一下树状数组的使用和前缀和、差分、树状数组这些简单的数据结构的区别和功能。
[LeetCode]1674. 使数组互补的最少操作次数(扫描 + 差分\树状数组)
标签:需要 使用 题解 owb pre ref ace 最小 turn
原文地址:https://www.cnblogs.com/enmac/p/14062417.html