题解 - 数组调整问题
问题描述
小R希望通过对数组进行有限的调整操作,使所有元素都落在给定的区间 [l, r] 内。调整操作包括选择一个元素加1,另一个元素减1。这种操作的目标是尽可能少地调整数组,使得所有元素都在规定的范围内。如果无法达到目标,则返回 -1。
解题思路
-
平均值初估:首先,我们需要确保最终数组的平均值要在
[l, r]的范围内。若数组的总和无法分配到每个元素在该范围内,则无论如何调整也无法实现目标:- 计算数组的总和
sum。 - 计算理想情况下每个元素的平均值,该平均值应当在
[l, r]中。如果sum / n < l,说明连最小要求的值都不能满足,返回 -1;如果(sum + n - 1) / n > r,说明即使将所有元素的总和加最大值,它也不能满足上限条件,也返回 -1。
- 计算数组的总和
-
确定需要的调整量:接下来的步骤是确定数组中每个元素相对于
l和r的偏差。这可以通过以下两部分来计算:ansl:统计所有小于l的元素需要增加的数量。ansr:统计所有大于r的元素需要减少的数量。
对于每一个元素,如果它小于
l,则必须增加到l,其所需的增量就是l - a[i]。如果元素大于r,则需要减少到r,其所需的减量就是a[i] - r。 -
计算最小操作次数:因为每次调整必须同时选两个元素,因此我们所需的操作次数实际上是需要将
ansl(需要增加的总量)和ansr(需要减少的总量)进行比较,最终的需要的最小操作次数是max(ansl, ansr)。- 这是因为我们只能通过加减的方式互补,换句话说,如果我们需要增加不少于
x,同时减少不少于y,那么最少的操作次数就是这两个值的最大值。
- 这是因为我们只能通过加减的方式互补,换句话说,如果我们需要增加不少于
代码实现
以下是实现该思路的 C++ 代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
int solution(int n, int l, int r, std::vector<int> a) {
int sum = accumulate(a.begin(), a.end(), 0);
// 检查总和的平均值是否在 [l, r] 的范围内
if (sum / n < l || (sum + n - 1) / n > r) {
return -1; // 平均值不在范围的情况下返回 -1
}
int ansl = 0, ansr = 0;
// 计算需要增加和减少的值
for (int i = 0; i < n; i++) {
if (a[i] < l) {
ansl += l - a[i]; // 需要加的值
}
if (a[i] > r) {
ansr += a[i] - r; // 需要减的值
}
}
return max(ansl, ansr); // 返回调整所需的最大步数
}
int main() {
std::cout << (solution(2, 3, 5, {1, 2}) == -1) << std::endl; // -1
std::cout << (solution(3, 4, 6, {3, 6, 5}) == 1) << std::endl; // 1
std::cout << (solution(4, 2, 8, {1, 10, 2, 6}) == 2) << std::endl; // 2
}