题意:有n个盒子,围城一个环,每个盒子里可能有糖果或无糖果,总糖果不超过N,要求,把盒子中糖果数超过1的移到无糖果的盒子中,问最少几步。
分析:把有糖果数为val的盒子,拆成val个装一个糖果的盒子,编号num1从1开始累加,但他们在同一点。然后就是与每个盒子匹配,用km,这里要求最少几步,就拿距离来建图。
w[i][j] 表示第i个糖果的盒子,到第j个盒子的距离,由于是求最少,那么w[i][j]=-w[i][j],求出最大,然后答案取反就是最小。
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <string.h>
#include <map>
#include <set>
using namespace std;
const int maxn=505;
const int inff=1<<30;
int w[maxn][maxn],n,m;
int lx[maxn],ly[maxn];
int r[maxn],slack[maxn],val;
bool s[maxn],t[maxn];
bool match(int i)
{
s[i]=true;
for(int j=1;j<=m;j++)
{
int temp=lx[i]+ly[j]-w[i][j];
if(temp==0&&!t[j])
{
t[j]=true;
if(!r[j]||match(r[j]))
{
r[j]=i;
return true;
}
}
if(!t[j])
slack[j]=min(slack[j],temp);
}
return false;
}
void update()
{
int i,j;
int a=inff;
/* for(i=1;i<=n;i++)
{
if(s[i])
for(j=1;j<=m;j++)
{
if(!t[j])
{
a=min(a,lx[i]+ly[j]-w[i][j]);
}
}
}*/
for(j=1;j<=m;j++)
if(!t[j])
a=min(a,slack[j]);
for(i=1;i<=n;i++)
{
if(s[i])lx[i]-=a;
}
for(j=1;j<=m;j++)
if(t[j])ly[j]+=a;
}
void km()
{
int i,j;
memset(r,0,sizeof r);
memset(lx,0,sizeof lx);
memset(ly,0,sizeof ly);
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
lx[i]=max(lx[i],w[i][j]);
}
}
for(i=1;i<=n;i++)
{
for( j=1;j<=m;j++)
slack[j]=inff;
while(1)
{
memset(s,0,sizeof s);
memset(t,0,sizeof t);
if(match(i))break;
else update();
}
}
}
int x[maxn],y[maxn];
int main()
{
int num1,num2;
while(scanf("%d",&n)!=EOF)
{
/*****************************///拆点
num1=num2=1;
for(int i=1;i<=n;i++)
{
scanf("%d",&val);
if(val)
{
for(int j=1;j<=val;j++)
x[num1++]=i;
y[num2++]=i;
}
else y[num2++]=i;
}
for(int i=1;i<num1;i++)
{
for(int j=1;j<num2;j++)
{
w[i][j]=min(max(x[i],y[j])-min(x[i],y[j]),min(x[i],y[j])+n-max(x[i],y[j]));
if(x[i]==y[j])w[i][j]=0;
w[i][j]=-w[i][j];
}
}
/******************************/
n=num1-1,m=num2-1;
km();
int ans=0;
for(int j=1;j<=m;j++)
{
if(r[j])
{
ans+=w[r[j]][j];
}
}
printf("%d\n",-ans);
}
return 0;
}