题目大意:
n个盒子摆成了一个环形,有的盒子中有糖果,有的则没有糖果。现在要移动糖果使得每个盒子中至多有一个糖果,每次只可以移动一个糖果到相邻盒子,问最小移动次数。
思路:
不要考虑怎么去移动,发现最后的状态一定是每个盒子中多出来的糖果移动到有一些空的盒子上面去。每一个多出来的糖果都需要被移动,且每个空盒子只能有一个糖果。我们把每一个多出来的糖果作为x方的一个点,每一个空白的盒子作为y方的一个点,每个糖果都可以向每一个盒子连一条权值为最小移动次数的边,然后跑KM即可。
/*============================
* Author : ylsoi
* Problem :hdu2282
* Algorithm : KM
* Time : 2018.6.17
* =========================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
using namespace std;
void File(){
freopen("hdu2282.in","r",stdin);
freopen("hdu2282.out","w",stdout);
}
template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;}
template<typename T>bool chkmin(T &_,T __){return _>__ ? (_=__,1) : 0;}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i<=b;++i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf INT_MAX
const int maxn=500+10;
int n,a[maxn],dis[maxn][maxn],nx,ny,num[maxn];
int lx[maxn],ly[maxn],slack[maxn],be[maxn],ans;
bool visx[maxn],visy[maxn];
void init(){
nx=ny=ans=0;
mem(ly);
mem(be);
REP(i,1,n)scanf("%d",&a[i]);
REP(i,1,n)if(!a[i])num[i]=++ny;
REP(i,1,n)REP(j,1,a[i]-1){
++nx;
lx[nx]=inf;
REP(k,1,n)if(!a[k]){
int d=((i-k)%n+n)%n;
dis[nx][num[k]]=min(d,n-d);
chkmin(lx[nx],min(d,n-d));
}
}
}
bool Hungary(int u){
visx[u]=1;
REP(i,1,ny){
if(visy[i])continue;
int gap=dis[u][i]-lx[u]-ly[i];
if(!gap){
visy[i]=1;
if(!be[i] || Hungary(be[i])){
be[i]=u;
return true;
}
}
else chkmin(slack[i],gap);
}
return false;
}
void KM(){
REP(i,1,nx){
REP(j,1,ny)slack[j]=inf;
while(1){
mem(visx);
mem(visy);
if(Hungary(i))break;
int gap=inf;
REP(j,1,ny)if(!visy[j])
chkmin(gap,slack[j]);
REP(j,1,nx)if(visx[j])lx[j]+=gap;
REP(j,1,ny)if(visy[j])ly[j]-=gap;
else slack[j]-=gap;
}
}
REP(i,1,ny)if(be[i])ans+=lx[be[i]]+ly[i];
printf("%d\n",ans);
}
int main(){
File();
while(~scanf("%d",&n)){
init();
KM();
}
return 0;
}