分析:
连边,每个村民->每个房子,边权为需要走的步数
一个村民只能选一个房子,
建出来的图是 左半部是村民,右半部是房子的二分图
跑km算法就能算出来最短步数。
km算法求的是最大权
在建图的时候把权值取相反数
求出来的最大权就是最小权的相反数
输出结果的相反数即可
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
typedef long long ll;
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int maxm=105;
struct Node{
int x,y;
}a[maxm],b[maxm];//保存坐标
int l[maxm],r[maxm];//两边的期望值
int g[maxm][maxm];//权值
int lmark[maxm];//标记
int rmark[maxm];//标记
int need[maxm];//右半边还差多少能够被匹配
int now[maxm];//当前匹配
int acnt;//左半边数量
int bcnt;//右半边数量
int n,m;
int dfs(int x){
lmark[x]=1;
for(int i=1;i<=bcnt;i++){
if(rmark[i])continue;
int t=l[x]+r[i]-g[x][i];
if(t==0){
rmark[i]=1;
if(now[i]==-1||dfs(now[i])){
now[i]=x;
return 1;
}
}else{
need[i]=min(need[i],t);
}
}
return 0;
}
int km(){
memset(now,-1,sizeof now);
memset(r,0,sizeof r);
for(int i=1;i<=acnt;i++){
l[i]=g[i][1];
for(int j=2;j<=bcnt;j++){
l[i]=max(l[i],g[i][j]);
}
}
for(int i=1;i<=acnt;i++){
for(int j=1;j<=bcnt;j++){
need[j]=inf;
}
while(1){
memset(lmark,0,sizeof lmark);
memset(rmark,0,sizeof rmark);
if(dfs(i))break;
int d=inf;
for(int j=1;j<=bcnt;j++){
if(!rmark[j])d=min(d,need[j]);
}
for(int j=1;j<=acnt;j++){
if(lmark[j])l[j]-=d;
}
for(int j=1;j<=bcnt;j++){
if(rmark[j])r[j]+=d;
else need[j]-=d;
}
}
}
int ans=0;
for(int i=1;i<=bcnt;i++){
ans+=g[now[i]][i];
}
return ans;
}
int getd(Node a,Node b){
return abs(a.x-b.x)+abs(a.y-b.y);
}
int main(){
while(cin>>n>>m&&(n+m)){
acnt=0;
bcnt=0;
char t;
getchar();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
t=getchar();//这题不需要存下整个图,只需要存有用的就行了
if(t=='m'){
a[++acnt].x=i;
a[acnt].y=j;
}else if(t=='H'){
b[++bcnt].x=i;
b[bcnt].y=j;
}
}
getchar();
}
for(int i=1;i<=acnt;i++){
for(int j=1;j<=bcnt;j++){
g[i][j]=-getd(a[i],b[j]);//权值取相反数
}
}
cout<<-km()<<endl;//输出相反数
}
return 0;
}