2017CCPC哈尔滨 H:A Simple Stone Game

题目链接: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;
}



你可能感兴趣的:(数列)