E1.Send Boxes to Alice(Easy Version)//中位数

发送盒子给Alice(简单版本)##

题意:准备n个盒子放巧克力,从1到n编号,初始的时候,第i个盒子有ai个巧克力。
Bob是一个聪明的家伙,他不会送n个空盒子给Alice,换句话说,每个盒子里面都有巧克力。
Alice不喜欢互质集,如果这里存在一个整数k > 1,每个盒子里的巧克力数量都能被k整除,那么Alice会很高兴。
Alice不介意这里有空盒子。

每次操作可以把一个盒子里的巧克力放置在相邻两个盒子里,即第i个盒子里的巧克力可以放在第i + 1和i - 1盒子里。
如果这里没有方法使得Alice高兴,就打印-1。
求最小的操作次数。
分析:如何确定具体的k?
一:我们可以枚举k,k从1到sum
二:如何求具体的操作次数,显然我们可以把所有的巧克力分成好几段,能被k整除,如果存在一段x * k,虽然能被整除,但是所需要移动的巧克力就更多了。
三:我们每段组合k个巧克力,移动的时候,把所有的数往中间移动,所需要移动的次数最少。(中位数的性质)

从一二可以得出,k是sum的质因子,所以我们只需要枚举质因子就可以了

假设中位数位置为median 每个巧克力的位置为ai,那么总共的操作次数为C = |a1 - median| + |a2 - median| +...+ |ai-1 - median| + |ai - median|
//货仓选址
当长度i为奇数时,中点在(i + 1) / 2时最优,当长度i为偶数时,中点在i / 2 ~ i / 2 + 1任意两个位置即可

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;

const int N = 100005;
int n;
int a[N];
vector<int> v;

long long cost(int p)
{
	long long res = 0;
	for (int i = 0; i < v.size(); i += p)
	{
		int mid = v[(i + i + p - 1) / 2];
		for (int j = i; j < i + p; ++j)
			res += abs(v[j] - mid);
	}
	return res;
}


int main()
{
	scanf("%d", &n);

	for (int i = 1; i <= n; ++i)
	{
		scanf("%d", &a[i]);
		if (a[i] == 1)
			v.push_back(i);//每个1的位置
	}

	if (v.size() == 1)
	{
		puts("-1");
		return 0;
	}

	long long ans = 1e20;

	int tmp = v.size(), k = 2;//枚举k

	while (k * k <= tmp)
	{
		if (tmp % k == 0)
		{
			ans = min(ans, cost(k));
			while (tmp % k == 0)
				tmp /= k;
		}
		++k;
	}

	if (tmp > 1)
		ans = min(ans, cost(tmp));
	printf("%I64d
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/pixel-Teee/p/11963280.html