[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 <iostream>
#include <cmath>
#include <map>
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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WitheredSakura_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值