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,从1直到sqrt(sum),sum是所有巧克力数量的总和,就可以确定具体的因子
二:如何求具体的操作次数,显然我们可以把所有的巧克力分成好几段,能被k整除,如果存在一段x * k,虽然能被整除,但是所需要移动的巧克力就更多了。
三:我们每段组合k个巧克力,移动的时候,把所有的数往中间移动,所需要移动的次数最少。(中位数的性质)
假设中位数位置为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\n", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/pixel-Teee/p/11963280.html