題目描述
給你一個序列,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
*/