码迷,mamicode.com
首页 > 编程语言 > 详细

[LeetCode]1674. 使数组互补的最少操作次数(扫描 + 差分\树状数组)

时间:2020-12-04 11:33:41      阅读:9      评论:0      收藏:0      [点我收藏+]

标签:需要   使用   题解   owb   pre   ref   ace   最小   turn   

1674. 使数组互补的最少操作次数

? LeetCode第217周赛的第三题,比赛时卡了一个小时,没有想到O(n)的做法。对差分不熟悉,但是最关键的还是扫描的思路没有想到。由于这道题有这么几个点比较重要,觉得应该特别记录一下。

  1. 扫描:比赛时我也想到了当选定和K处于个个区间[2, lo]、[lo, hi]、[sum, sum]时的状态。可是我把对于每个nums[i]的lo、hi、sum都看成独立的一套。没有联系起来。题解里面把所有数放在数轴上的思路是我第一次见到这种思想。
  2. 差分:这题不一定一定要用差分,只是利用了差分的区间更新、单点查询的性质。实际上也可也用树状数组,或者更复杂的线段树(还没学过)。只是这里差分就可以完成。
//
// 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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!