[Daimayuan] 一半相等(C++,数学)

给定 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 4n100,数据保证 n n n 为偶数

− 1 0 6 ≤ a i ≤ 1 0 6 −10^6≤a_i≤10^6 106ai106

解题思路

既然有一半的元素能够在相同的 k k k的作用下达到相等即达到一个共同的值。

那么这一半的元素到这个共同值的距离可以被 k k k整除,而要求 k k k尽可能的大,故类似于最大公约数。

(当数组中已经有就有一半的元素相等,那么可以直接输出 − 1 -1 1。)

但是我们要求哪一半元素?又是要求到谁的距离呢?

(注意是距离,也就是差值的绝对值,因为转化是双向的,如 1 − 2 = − 1 , − 1 − ( − 2 ) = 1 1-2=-1,-1-(-2)=1 12=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 n100):

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;
}

你可能感兴趣的:(数学,c++,算法,开发语言,数学)