トピックへのリンク主なアイデア:
配列Aが与えられると、各演算は配列内の数値に対して+1または-1演算を実行できます(演算は正の整数でなければなりません)。最後の配列がこの配列のすべての数値の最大公約数を満たすようにすることは1ではありません。操作の最小数を尋ねます。
問題解決のアイデア:(
マジックランダムアルゴリズム)
最大公約数が2であるとすると、各数値は最大で1回のみ操作する必要があるため、操作の数は最大でnです。したがって、演算回数≤1は≥n / 2である必要があります。数xの演算回数≤1はx、x + 1、x-1の3つのケースになるため、演算数≤1をランダム化でき、この数より小さいランダム性の確率は1 / 2 T. Tの数が大きくなると、確率は非常に小さくなり、ほとんど不可能になります。それぞれの乱数について、彼の3つの状況を直接判断します。それぞれの状況は、彼の素因数を列挙し、必要な操作の数を激しく計算し、最後に最小値をとります。複雑さは
O(T *(sqrt(最大)+ n *ログ(最大)))です。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
mt19937 rng_32(chrono::steady_clock::now().time_since_epoch().count());
ll a[200005];
int n;
ll cal_ans(ll x)
{
ll ret=0;
for (int i=0;i<n;i++)
{
ll tmp=a[i]%x;
//防止出现0
if (a[i]!=tmp)
ret+=min(tmp,x-tmp);
else
ret+=x-tmp;
}
return ret;
}
//计算质因子
ll fac(ll x)
{
ll ret=1e18;
ll en=sqrt(x+1ll);
for(ll i=2;i<=en;i++)
{
if (x%i==0)
{
ret=min(ret,cal_ans(i));
while(x%i==0)
x/=i;
if (x==1)
break;
}
}
if (x>1)
ret=min(ret,cal_ans(x));
return ret;
}
int main()
{
cin>>n;
for (int i=0;i<n;i++)
scanf("%I64d",&a[i]);
int T=10;
ll ans=1e18;
while (T--)
{
ll pos=rng_32()%n;
//处理三种情况
if (a[pos]>2)
ans=min(ans,fac(a[pos]-1ll));
ans=min(ans,fac(a[pos]));
ans=min(ans,fac(a[pos]+1ll));
}
cout<<ans;
}