题目描述
给你一个序列,ai表示i位置有多少个糖果,每次你可以将一个位置i的一个糖果给位置i+1或者位置i-1(如果存在的话),现在你需要对糖果进行移动,对于每个有糖果的堆,糖果的数量必须是一个数k(k>1)的倍数,允许空堆的存在,问你最少需要移动多少步?
思路
假设n堆中有m个为1的糖果,也就是总共为m个糖果,如果可以不全部汇总,我肯定可以将m个糖果分为k堆,k为m的因子,比如1 1 0 0 1 1 0 0 1 1,有6个糖果,当k=2时两两化成一堆最优。
但是k我们不能确定,比如1 1 1 0 0 0 0 1 1 1 ,这样当k=3时才是最优的,所以我们枚举m的因子k,对于每个因子k,我们每次合并肯定拿前k个合并最优,暴力枚举一下,用中位数计算答案就好了。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn=1e5+10;
int a[maxn];
vector<int>G;
int n;
ll calculate(int x){
//printf("debug %d ",x);
if(x==1)return 1e18;
ll ans=0;
int pos=0;
for(int i=0;i<G.size();){
vector<int>v;
int cnt=x;
for(int j=pos+1;j<=n;j++){
if(a[j]){
v.push_back(j);
cnt--;
}
if(!cnt){
pos=j;
break;
}
}
ll center=0;
if(v.size()&1){
center=v[v.size()/2];
}
else{
center=(v[v.size()/2-1]+v[v.size()/2])/2;
}
for(int j=0;j<v.size();j++){
ans+=abs(1ll*v[j]-center);
}
i+=x;
}
//printf("ans %lld\n",ans);
return ans;
}
signed main(){
scanf("%d",&n);
int sum=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum+=a[i];
if(a[i])G.push_back(i);
}
if(sum<=1){
puts("-1");
return 0;
}
ll ans=1e18;
for(int i=1;i*i<=sum;i++){
if(sum%i==0){
int a=i;
ans=min(ans,calculate(a));
if(sum/a!=a){
int b=sum/a;
ans=min(ans,calculate(b));
}
}
}
printf("%lld\n",ans);
return 0;
}
/*
10
1 1 1 0 0 0 0 1 1 1
*/