难度:中等
给你一个长度为 偶数 n 的整数数组 nums 和一个整数 limit 。每一次操作,你可以将 nums 中的任何整数替换为 1 到 limit 之间的另一个整数。
如果对于所有下标 i(下标从 0 开始),nums[i] + nums[n - 1 - i] 都等于同一个数,则数组 nums 是 互补的 。例如,数组 [1,2,3,4] 是互补的,因为对于所有下标 i ,nums[i] + nums[n - 1 - i] = 5 。
返回使数组 互补 的 最少 操作次数。
示例 1:
输入:nums = [1,2,4,3], limit = 4
输出:1
解释:经过 1 次操作,你可以将数组 nums 变成 [1,2,2,3](加粗元素是变更的数字):
nums[0] + nums[3] = 1 + 3 = 4.
nums[1] + nums[2] = 2 + 2 = 4.
nums[2] + nums[1] = 2 + 2 = 4.
nums[3] + nums[0] = 3 + 1 = 4.
对于每个 i ,nums[i] + nums[n-1-i] = 4 ,所以 nums 是互补的。
示例 2:
输入:nums = [1,2,2,1], limit = 2
输出:2
解释:经过 2 次操作,你可以将数组 nums 变成 [2,2,2,2] 。你不能将任何数字变更为 3 ,因为 3 > limit 。
示例 3:
输入:nums = [1,2,1,2], limit = 2
输出:0
解释:nums 已经是互补的。
提示:
n == nums.length
2 <= n <= 105
1 <= nums[i] <= limit <= 105
n 是偶数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-moves-to-make-array-complementary
// 区间更新,单点求值
// 单点的值用前缀和表示 即 c[i] = a[1] + a[2] + ... + a[i - 1] + a[i]
// 区间[x, y]增加k 即a[x] + k, a[y + 1] - k,这样对于x->y的前缀和都加k,y+1前缀和并没变
class BIT{
public:
int n, m;
vector<int> c;
BIT(int _n) : n(_n), c(_n + 1){
}
int lowbit(int x){
return x & (-x);
}
//更新单点信息
void _update(int i, int k){
while(i <= n){
c[i] += k;
i += lowbit(i);
}
}
int getSum(int i){
int res = 0;
while(i > 0){
res += c[i];
i -= lowbit(i);
}
return res;
}
//区间[x, y]增加k
void update(int x, int y, int k){
_update(x, k);
_update(y + 1, -k);
}
};
class Solution {
public:
int minMoves(vector<int>& nums, int limit) {
BIT bit(limit * 2);
int size = nums.size();
for(int i = 0; i < size / 2; i++){
int minNum = min(nums[i], nums[size - 1 - i]);
int maxNum = max(nums[i], nums[size - 1 - i]);
bit.update(1, minNum, 2);
bit.update(maxNum + limit + 1, limit * 2, 2);
bit.update(minNum + 1, maxNum + limit, 1);
bit.update(nums[i] + nums[size - 1 - i],nums[i] + nums[size - 1 - i], -1);
}
int minCount = size;
int minSum = 0;
int a = *min_element(nums.begin(), nums.end());
int b = *max_element(nums.begin(), nums.end());
for(int i = a; i <= b + limit; i++){
if(bit.getSum(i) < minCount){
minCount = bit.getSum(i);
minSum = i;
}
}
int ans = 0;
for(int i = 0; i < size / 2; i++){
if(nums[i] + nums[size - 1 - i] == minSum)
continue;
int minNum = min(nums[i], nums[size - 1 - i]);
int maxNum = max(nums[i], nums[size - 1 - i]);
if(minNum + 1 > minSum || maxNum + limit < minSum)
ans += 2;
else
ans += 1;
}
return ans;
}
};
class Solution {
public:
int minMoves(vector<int>& nums, int limit) {
vector<int> count(limit * 2 + 2, 0);
int size = nums.size();
for(int i = 0; i < size / 2; i++){
int a = max(nums[i], nums[size - 1 - i]);
int b = min(nums[i], nums[size - 1 - i]);
int sum = nums[i] + nums[size - 1 - i];
// [0, b] and [a + limit + 1, limit * 2 + 1] need 2
// [b + 1, sum - 1] and [sum + 1, a + limit] need 1
count[0] += 2, count[b + 1] -= 2;
count[a + limit + 1] += 2, count[limit * 2 + 1] -= 2;
count[b + 1] += 1, count[sum] -= 1;
count[sum + 1] += 1, count[a + limit + 1] -= 1;
}
int res = INT_MAX;
for(int i = 1; i <= limit * 2; i++){
count[i] += count[i - 1];
if(count[i] < res)
res = count[i];
}
return res;
}
};
两种方法底层的区间更新,单点查询的原理都是差不多的。
原文:https://www.cnblogs.com/bgyx-hyyy/p/14061837.html