题目:
题解:
转来KM啦,这是一道裸题哦
KM算法是用来求二分图最大带权匹配的,这里安利一下讲的比较好的blog,对于这道题来说只需要把边权全都设为负的求个最大值然后取反就是最小的路咯
如果你不每次清空l[x]和r[x],而是加一个编号的话,效率更好哦
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define INF 1e9
using namespace std;
struct hh{int x,y;}ren[105],hou[105];
char st[105];
int e[105][105],lx[105],ly[105],n,belong[105],vis[205],id,toth;
bool find(int i,int id)
{
vis[i]=id;
for (int j=1;j<=n;j++)
if (lx[i]+ly[j]==e[i][j] && vis[j+n]!=id)
{
vis[j+n]=id;
if (!belong[j] || find(belong[j],id))
{
belong[j]=i;
return 1;
}
}
return 0;
}
void change(int id)
{
int i,j,a=INF;
for (i=1;i<=n;i++)
if (vis[i]==id)
for (j=1;j<=n;j++)
if (vis[j+n]!=id) a=min(a,lx[i]+ly[j]-e[i][j]);
for (i=1;i<=n;i++)
{
if (vis[i]==id) lx[i]-=a;
if (vis[i+n]==id) ly[i]+=a;
}
}
int KM()
{
int i,j,ans=0;
memset(vis,0,sizeof(vis));
for (i=1;i<=n;i++)
{
belong[i]=0;lx[i]=ly[i]=0;
for (j=1;j<=n;j++) lx[i]=max(lx[i],e[i][j]);
}
for (i=1;i<=n;i++)
while(1)
{
++id;
if (find(i,id)) break;
else change(id);
}
for (i=1;i<=n;i++) ans+=e[belong[i]][i];
return ans;
}
int main()
{
int nn,m,i,j;
scanf("%d%d",&nn,&m);
while (nn)
{
n=toth=0;
for (i=1;i<=nn;i++)
{
scanf("%s",st+1);
for (j=1;j<=m;j++)
if (st[j]=='m') ren[++n].x=i,ren[n].y=j;
else if (st[j]=='H') hou[++toth].x=i,hou[toth].y=j;
}
for (i=1;i<=n;i++)
for (j=1;j<=toth;j++)
e[i][j]=-(abs(ren[i].x-hou[j].x)+abs(ren[i].y-hou[j].y));
printf("%d\n",-KM());
scanf("%d%d",&nn,&m);
}
}