第一次做KM算法的题目,也许没能好好理解匈牙利算法,对这个KM算法几乎看不懂,上网搜了一下模板,这题几乎是抄的,看来要好好理解。
题意:给你一个地图,里面有人和有屋(测试数据可以看成是人数等于屋数),一个人只能想上下左右移动,求所有人移动的次数最少的情况下,所有人都能进入屋子,当然可以路过屋子而不进去。
解题思路:这题明显就是求最小权匹配的,只要把步数变为负数,就是求最大权匹配嘛,直接KM算法,模板过了,问题是建图要小心,注意好细节,这题其实不会很难的,KM算法要理解理解吖
31msG++代码
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#include<cstdio>
using namespace std;
const int INF=1<<26;
typedef struct node{
int x,y;
}node;
int n,m,numh,numm,ans;
int mj[105][105];
node pionth[105],piontm[105];
bool vx[105],vy[105];
int slack[105],match[105],lx[105],ly[105];
bool Find(int x)
{
vx[x]=true;
for(int j=1;j<=numm;j++)
{
if(vy[j]==false)
{
if(lx[x]+ly[j]==mj[x][j])
{
vy[j]=true;
if(match[j]==-1||Find(match[j]))
{
match[j]=x;
return true;
}
}
else
slack[j]=min(slack[j],lx[x]+ly[j]-mj[x][j]);
}
}
return false;
}
void KM_match()
{
int temp;
for(int i=1;i<=numh;++i)//初始化为大负数
lx[i]=-INF;
memset(ly,0,sizeof(ly));
for(int i=1;i<=numh;i++)
for(int j=1;j<=numm;j++)
lx[i]=max(lx[i],mj[i][j]);
for(int i=1;i<=numh;i++)
{
for(int j=1;j<=numm;j++)
slack[j]=INF;
memset(vx, false, sizeof(vx));
memset(vy, false, sizeof(vy));
while(!Find(i))
{
temp = INT_MAX;
for(int j=1;j<=numm;++j)
if(!vy[j])
temp = min(temp,slack[j]);
for(int j=1;j<=numm;++j)
{
if(vx[j])
lx[j]-=temp;
if(vy[j])
ly[j]+=temp;
else
slack[j]-=temp;
}
memset(vx, false, sizeof(vx));
memset(vy, false, sizeof(vy));
}
}
}
int main()
{
int i,j;
char ch;
while(scanf("%d %d",&n,&m),n||m)
{
memset(match, -1, sizeof(match));
ans=numh=numm=0;
for(i=0;i<n;i++)
{
getchar();
for(j=0;j<m;j++)
{
ch=getchar();
if(ch=='H')
{
numh++;
pionth[numh].x=i;
pionth[numh].y=j;
}
else if(ch=='m')
{
numm++;
piontm[numm].x=i;
piontm[numm].y=j;
}
}
}//确定点
for(i=1;i<=numh;i++)
{
for(j=1;j<=numm;j++)
{
int d=abs(pionth[i].x-piontm[j].x)+abs(pionth[i].y-piontm[j].y);
mj[i][j]=0-d;
}
}//建图
/*for(i=1;i<=numh;i++)
{
for(j=1;j<=numm;j++)
{
printf("%d ",mj[i][j]);
}
printf("\n");
}*/
KM_match();
for(j=1;j<=numm;j++)
ans+=mj[match[j]][j];
printf("%d\n",0-ans);
}
}