题目大意:
有n个盒子,每个盒子中可能有巧克力,也可能没有巧克力,现在将这些盒子围成一个圈,然后每一单位时间可以取任意一个盒子中的一个巧克力挪到相邻的位子上。
问最少操作次数,使得每个盒子只有一个巧克力(当然允许有盒子没有巧克力)(题目保证巧克力总数<=n)。
思路:
联系以前写过的KM算法,不过是求最小权值匹配。
1、首先:将巧克力看成村民,将每个盒子看成房子。这样就出现了一个二分图。
2、然后每个巧克力对应到每一个盒子都要花费不同的步数,相当于配上权值。
3、问题就转化到:有<=n个巧克力,有n个盒子,将这些个巧克力分配到n个盒子中,每个盒子最多只能保存一个巧克力,问最少权值花费,那么其问题就是一个最小权值匹配问题。那么建图,跑KM即可。
4、因为我们要求的是最小匹配问题,那么我们可以取它的负值-a[i][j],使得原先的较小值变成了较大值,最小值变成了最大值,那么现在跑一遍最大权值匹配KM算法,得到的最大匹配,其实就是原图中的最小匹配。ans=-最大匹配值。
AC代码:
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int map1[666][666],val[666],match[666],lx[666],ly[666];
bool vx[666],vy[666];
int n,low;
void getmap()
{
memset(map1,0,sizeof(map1));
int cont=0;
for(int i=0;i<n;i++)//对每个盒子
{
for(int j=0;j<val[i];j++)//里的每一块巧克力(记为cont)
{
for(int k=0;k<n;k++)//移动到其它盒子
{
map1[cont][k]=min(n-abs(i-k),abs(i-k));//所需要的最短步数
}
cont++;
}
}
}
bool find(int x)
{
vx[x]=1;
for(int i=0;i<n;i++)
{
if(vy[i]==1) continue;
int tmp=lx[x]+ly[i]-map1[x][i];
if(!tmp)
{
vy[i]=1;
if(match[i]==-1||find(match[i]))
{
match[i]=x;
return 1;
}
}
else if(tmp<low) low=tmp;
}
return 0;
}
void KM()
{
memset(match,-1,sizeof(match));
memset(lx,0,sizeof(lx));
memset(ly,0,sizeof(ly));
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)//对构建好的二分图
{
map1[i][j]=-map1[i][j];//取负它的权值
lx[i]=max(lx[i],map1[i][j]);
}
}
for(int i=0;i<n;i++)
{
while(1)
{
low=0x3f3f3f3f;
memset(vx,0,sizeof(vx));
memset(vy,0,sizeof(vy));
if(find(i)) break;
for(int j=0;j<n;j++)
{
if(vx[j]) lx[j]-=low;
if(vy[j]) ly[j]+=low;
}
}
}
int sum=0;
for(int i=0;i<n;i++)
{
sum+=map1[match[i]][i];
}
cout<<-sum<<endl;
}
int main()
{
while(cin>>n)
{
for(int i=0;i<n;i++) cin>>val[i];//输入每个盒子里的巧克力数
getmap();//转化成图
KM();
}
return 0;
}
扫描二维码关注公众号,回复:
2347970 查看本文章