HDU 2282 Chocolate(二分图最优匹配)
http://acm.hdu.edu.cn/showproblem.php?pid=2282
题意:
有n个盒子摆成的环,每个盒子里有一定数量的巧克力,巧克力总数小于n,问最少移几步(每步等于把一块巧克力从第i格移动到了i+1或i-1格),使得每个盒子里巧克力数小于等于1,从第i个盒子移到第j个盒子的最小步数为min( n-abs(i-j), abs(i-j) )。
分析:
如果一个盒子里有5块巧克力,其实我们需要做的就是把这盒子里的4块巧克力分别移到4个空盒子里去.
建立二分图: 左边点集是每一块需要被移动的巧克力, 右边点集是每一个空的盒子. 对于左边第i块巧克力来说,它被移动到右边第j个空盒子的最小距离为X,那么就在左i点与右j点之间加一条X权值的边.
最终我们求得就是该二分图的最小权值匹配.(权值取负数就是最优权值匹配了)
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 500+5;
struct Max_Match
{
int n,m,W[maxn][maxn];
int Lx[maxn],Ly[maxn];
bool S[maxn],T[maxn];
int left[maxn];
bool match(int i)
{
S[i]=true;
for(int j=1;j<=m;j++)if(Lx[i]+Ly[j]==W[i][j] && !T[j])
{
T[j]=true;
if(left[j]==-1 || match(left[j]))
{
left[j]=i;
return true;
}
}
return false;
}
void update()
{
int a=1<<30;
for(int i=1;i<=n;i++)if(S[i])
for(int j=1;j<=m;j++)if(!T[j])
a=min(a, Lx[i]+Ly[j]-W[i][j]);
for(int i=1;i<=n;i++)if(S[i]) Lx[i]-=a;
for(int i=1;i<=m;i++)if(T[i]) Ly[i]+=a;
}
int solve(int n,int m)
{
this->n=n;
this->m=m;
memset(left,-1,sizeof(left));
memset(Lx,0,sizeof(Lx));
memset(Ly,0,sizeof(Ly));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
Lx[i]=max(Lx[i], W[i][j]);
for(int i=1;i<=n;i++)
{
while(true)
{
memset(S,0,sizeof(S));
memset(T,0,sizeof(T));
if(match(i)) break;
else update();
}
}
int ans=0;
for(int i=1;i<=m;i++)
if(left[i]!=-1) ans+=W[left[i]][i];
return -ans;//注意取负值
}
}KM;
int main()
{
int n;
while(scanf("%d",&n)==1)
{
int num1=0,num2=0;
int pos1[maxn],pos2[maxn];//pos1与pos2分别记录左右点集每个点的原始位置
for(int i=1;i<=n;i++)
{
int v;
scanf("%d",&v);
if(v>1)
{
while(v>1)
{
pos1[++num1]=i;
--v;
}
}
else if(v==0)
{
pos2[++num2]=i;
}
}
for(int i=1;i<=num1;i++)
for(int j=1;j<=num2;j++)
KM.W[i][j]= -min(abs(pos1[i]-pos2[j]), n-abs(pos1[i]-pos2[j]));//注意取负值
printf("%d\n",KM.solve(num1,num2));
}
return 0;
}