CF D2. Half of Same Codeforces Round #748 (Div. 3)

题意

简单版本题意:给定n(偶数)个数字,可以进行如下操作,选定一个数字k,对每个数字减去若干个k最终所有数字相等,求最大的k是多少,如果k可以任意大输出-1。
复杂版本题意:给定n(偶数)个数字,可以进行如下操作,选定一个数字k,对其中n/2个数字每个数字减去若干个k使得这n/2个数字相同,求最大的k,如果k可以任意大输出-1。

思路

在求解困难版本之前我们先看简单版本,所有数字要减去若干个k变成同一个数字。首先看两个数字的情况,假设有两个数字a,b(其中b>a),如果b-a不是0而且也不是k的整数倍,那么显然易见无论如何也不能通过让两个数字减去若干个k的操作使得a和b相等。
简单证明一下:如果b-a不是0而且不是k的整数倍,假设可以通过b减去若干个k和a减去若干个k使得两个数字相同,这里可以写一个二元一次方程,b-xk=a-yk,通过移项得到b-a=xk-yk也就是b-a=(x-y)k,因为b-a不是k的倍数,这里(x-y)一定不是整数,所以k一定要是b-a的因子,也就是b-a要么是0要么得是k的整数倍。

两个数字的情况看完了,对于简单版本的n个数字显然取的k一定要是每个数字和最小的那个数字的差的因子,否则无论如何也无法让n个数字变得相同,所以简单版本只需要取每个数字和最小的数字的差的最大公因子就可以了,如果所有数字相同就输出-1。所以简单版本的答案也就出来了,如下代码:

#include<bits/stdc++.h>
using namespace std;
int T;
int n;
int a[1000];
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
		sort(a+1,a+1+n);
		int t=a[2]-a[1];
		for(int i=3;i<=n;i++)
		{
			t=__gcd(t,a[i]-a[1]);
		}
		if(t==0)
		printf("-1\n");
		else
		printf("%d\n",t);

	}
return 0;
}

对于复杂版本要求选的k至少能让n/2个数字变的相同,而且需要k尽量大。我们可以枚举一个数字a,作为数组最小的数字,再枚举一个大于a的数字b,得到一个差d,枚举d的每个因子p作为k的候选值,如果大于a的数字中还有n/2-1个数字和a的差是p的整数倍或者是0,那么p就是满足条件的,按照这个方法枚举就可以得到答案。
主要的优化点在于得到差d之后,枚举d的每个因子,显然不是枚举i从1~d,这样子肯定超时,其实只需要枚举i从1 ~ sqrt(d)就可以了,取i和n/i作为p进行查找。

#include<bits/stdc++.h>
using namespace std;
int T;
int n;
int a[100];
map<int,int>mp;//存储每个数字的个数
bool chk(int v,int p)//判断大于等于第i个数字且与第i个数字的差是p的整数倍或为0的个数大于等于n/2-1返回1
{
	int ans=0;
	for(int i=p+1;i<=n;i++)
	{
		if((a[i]-a[p])%v==0)
		{
			ans++;//个数+1
		}
	}
	if(ans>=n/2-1)
	return 1;
	return 0;
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		mp.clear();
		scanf("%d",&n);
		int mx=0;
		for(int i=1;i<=n;i++)
		scanf("%d",&a[i]),mp[a[i]]++,mx=max(mp[a[i]],mx);
		if(mx>=n/2)//某个数字超过n/2个则k任意大
		printf("-1\n");
		else
		{
			sort(a+1,a+1+n);//a从小到大排序
			int ans=1;
			for(int i=1;i<=n;i++)//枚举最小的数a
			{
				for(int j=i+1;j<=n;j++)//枚举一个大于a的数b
				{
					int d=a[j]-a[i];//取d=b-a
					if(d==0)//如果b和a相同跳过
					continue;
					for(int p=1;p*p<=d;p++)//枚举d的所有因子
					{
						if(chk(p,i))//判断大于等于第i个数字且与第i个数字的差是p的整数倍或者是0的个数大于等于n/2-1返回1
						ans=max(ans,p);
						if(chk(d/p,i))
						ans=max(ans,d/p);
					}
				}
			}
			printf("%d\n",ans);
		}
	}

return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值