给定 n n n ( n n n 为偶数)个整数数组 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an
考虑这样的一个 k k k,每次操作选定一个 i i i,将 a i a_i ai 减少 k k k,执行多次(可能 0 0 0 次)后使得数组中至少有一半的元素相等,求最大的 k k k,如果这样的 k k k 为无穷大,输出 − 1 −1 −1
输入包含两行,第一行为一个正整数 n n n,表示数组大小。第二行为 n n n 个整数 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an
输出题意中的 k k k
8
-1 0 1 -1 0 1 -1 0
2
4 ≤ n ≤ 100 4≤n≤100 4≤n≤100,数据保证 n n n 为偶数
− 1 0 6 ≤ a i ≤ 1 0 6 −10^6≤a_i≤10^6 −106≤ai≤106
既然有一半的元素能够在相同的 k k k的作用下达到相等即达到一个共同的值。
那么这一半的元素到这个共同值的距离可以被 k k k整除,而要求 k k k尽可能的大,故类似于最大公约数。
(当数组中已经有就有一半的元素相等,那么可以直接输出 − 1 -1 −1。)
但是我们要求哪一半元素?又是要求到谁的距离呢?
(注意是距离,也就是差值的绝对值,因为转化是双向的,如 1 − 2 = − 1 , − 1 − ( − 2 ) = 1 1-2=-1,-1-(-2)=1 1−2=−1,−1−(−2)=1)
因为存在这样一种情形:一半的元素到 x x x的距离的gcd
,大于到 y y y的距离的gcd
。
例如: 2 , 5 2,5 2,5到 0 0 0的距离的最大公约数是 1 1 1,而到 − 1 -1 −1的距离的最大公约数是 3 3 3。
所以选择的目标位置会对最终的结果产生影响。
但是我们可以证明目标位置一定在给定的数组中:
直观起见,以下面这个数组为例,应该选择的一半元素是 2 , 5 , 8 , 8 2,5,8,8 2,5,8,8。
2 5 3 3 8 8
对此,我们可以选择 − 1 -1 −1作为目标位置,拿到最大公约数 3 3 3。
但是我们同样也可以选择 2 2 2作为目标位置,拿到最大公约数 3 3 3。
我们想不出一种比较好的算法来解决以上的两个问题,所以采用暴力搜索(毕竟 n ≤ 100 n\le100 n≤100):
1)尝试将每一个数作为目标位置;
2)计算其他所有数到目标位置的距离;
3)求出距离的所有的因子并统计其出现的频率;
4)保留最大的因子。
最后,AC代码如下:
#include
#include
#include
using namespace std;
const int max_n = 100;
int arr[max_n];
map<int, int>freqs;
void division(int x) {
int i;
for (i = 1; i * i < x; i++) {
if (x % i == 0) {
freqs[i]++;
freqs[x / i]++;
}
}
if (i * i == x) freqs[i]++;
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
int ans = 0;
for (int i = 0; i < n; i++) {
const int temp = arr[i];
int cnt = 0;
for (int j = 0; j < n; j++) {
if (temp == arr[j]) cnt++;
else division(abs(temp - arr[j]));
}
if (cnt >= n / 2) {
cout << -1 << endl;
return 0;
}
for (auto iter : freqs)
if (iter.second + cnt >= n / 2)
ans = max(ans, iter.first);
freqs.clear();
}
cout << ans << endl;
return 0;
}