题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1008&cid=784
题意:
给你n个正整数,每次可以将某个数-1,另一个数+1,问最少操作多少次可以使得所有数都是x的倍数
x任意,但不能等于1
思路:
求出所有数之和sum,显然x一定要是sum的约数,并且最优解时x一定可以是质数
显然这样的x最多不超过30个,所以可以暴力枚举所有满足上述条件的x
处理的时候算出所有的b[i] = a[i]-a[i]/x*x
然后将b[]从小到大排序,之后头尾模拟一遍就可以得出当前最小移动次数
所有移动次数中取最小就是答案
注意一个坑点,x可能会很大很大,不过a[]不会超过100000,所以这时候一定是将所有数全部移动到最大的那个数上,初始化ans = sum-max(a[i])就好
//2017CCPC哈尔滨--F
#include
#include
using namespace std;
#define LL long long
int a[100005], b[100005], flag[100005] = {1,1};
int main(void)
{
LL sum, ans, now;
int T, n, i, j, bet, p, q;
for(i=2;i<=100000;i++)
{
if(flag[i])
continue;
for(j=i+i;j<=100000;j+=i)
flag[j] = 1;
}
scanf("%d", &T);
while(T--)
{
sum = bet = 0;
scanf("%d", &n);
for(i=1;i<=n;i++)
{
scanf("%d", &a[i]);
sum += a[i];
bet = max(bet, a[i]);
}
ans = sum-bet;
for(i=1;i<=100000;i++)
{
if(sum%i==0 && flag[i]==0)
{
for(j=1;j<=n;j++)
b[j] = a[j]-a[j]/i*i;
sort(b+1, b+n+1);
now = 0;
p = 1, q = n;
while(pi)
{
now += i-b[q];
b[p] -= i-b[q];
q--;
}
else
{
now += b[p];
b[q] += b[p];
p++;
}
}
ans = min(ans, now);
}
}
printf("%lld\n", ans);
}
return 0;
}