使陣列互補的最少操作次數(樹狀陣列/差分陣列)
阿新 • • 發佈:2020-11-30
難度:中等
給你一個長度為 偶數 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;
}
};
兩種方法底層的區間更新,單點查詢的原理都是差不多的。