1. 题目描述
假设你有一个长度为n的整数数组,现在告诉你可以执行以下操作之一,每次操作可以将数组中的一个元素加1或减1。
你的任务是找到最小的操作次数,使得该数组中的所有元素相等。
2. 解法分析
2.1 简化问题
首先,我们将问题简化一下,先考虑只能对某个元素进行加1操作的情况。我们假设数组的元素为a[1],a[2],...,a[n],那么可以将问题转化为求所有元素与数组中位数之差的绝对值之和。
假设数组是升序排列的,那么数组中位数的位置mid为mid=n/2+1。如果n是偶数,那么可以任意选择mid为mid=n/2或者mid=n/2+1。如果n是奇数,mid=n/2+1。
2.2 证明
为了证明这个结论,我们可以先以下面的例子来看,
a[1] = 1, a[2] = 4, a[3] = 6, a[4] = 8, a[5] = 9
我们先找到数组的中位数为6。下面列举一些可以将数组所有元素改为6的方案。
对a[1]进行4次加1操作,对a[2]进行2次加1操作,对a[3]进行0次操作,对a[4]进行2次减1操作,对a[5]进行3次减1操作。
对a[1]进行3次加1操作,对a[2]进行1次加1操作,对a[3]进行1次减1操作,对a[4]进行1次减1操作,对a[5]进行3次减1操作。
我们可以发现,无论哪种方案,每次操作都会将一个元素与6之间的差距减小1。因此,只要所有元素与6之间的差距能够消失,那么所有元素就会相等。而将所有元素与6之间的差距的绝对值求和,就等价于将所有元素调整到6之后,所有元素之间的差距的绝对值之和。
因此,我们只要找出数组的中位数,将所有元素调整到中位数,再求出所有元素与中位数之间的差距的绝对值之和即可。
2.3 扩展问题
如果我们允许对任意一个元素进行加1或减1操作,那该怎么办呢?
我们假设所有元素的平均值是avg。那么可以证明,将所有元素调整到avg之后,所有元素之间的差距的绝对值之和就是操作次数的下限。
同样是以上面的例子为例,如果我们将所有元素调整到5,那么操作次数如下:
a[1] = 1, a[2] = 4, a[3] = 6, a[4] = 8, a[5] = 9
→ a[1] = 5, a[2] = 5, a[3] = 5, a[4] = 5, a[5] = 5
→ 4 + 1 + 1 + 3 + 4 = 13
由此可见,无论怎样调整,操作次数都大于13。
3. 代码实现
根据以上分析,代码实现如下:
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
sort(a.begin(), a.end());
int mid = a[n / 2];
int ans = 0;
for (int i = 0; i < n; ++i) {
ans += abs(a[i] - mid);
}
cout << ans << endl;
return 0;
}
4. 总结
本题的解法并不难,但需要注意一些细节。在实现时,需要注意:
数组长度n是奇数或偶数的情况;
数组元素可能有重复的情况;
求绝对值时不要使用错误的数据类型(例如int)。
学会本题的解法,有助于理解一些数论问题,比如中位数的性质等。同时,求解此类问题的思路也有参考价值,希望读者可以从中受益。