就是最基本的二分图最优匹配,将每个人向每个房子建一条边,权值就是他们manhattan距离。然后对所有权值取反,求一次最大二分图最优匹配,在将结果取反就行了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define Maxn 110 using namespace std; int n;//点的数目 int lx[Maxn],ly[Maxn]; //顶点标号 int weight[Maxn][Maxn];// 边的权值 int slack[Maxn];// Y(i)的松弛函数 int sx[Maxn],sy[Maxn]; //标记X,Y中的顶点是否在交错路径上 int match[Maxn];//Y的匹配 struct Point{ int x,y; }house[Maxn],man[Maxn]; int Dis(Point a,Point b) { return abs(a.x-b.x)+abs(a.y-b.y); } int dfs(int u) { sx[u]=1; int i; for(i=1;i<=n;i++) { if(!sy[i]&&lx[u]+ly[i]==weight[u][i])//如果在相等子图中,且未被访问 { sy[i]=1; if(match[i]==-1||dfs(match[i])) { match[i]=u; return 1; } } else if(!sy[i])//Y(i)不在交错路径当中 slack[i]=min(slack[i],lx[u]+ly[i]-weight[u][i]); } return 0; } int bestmatch(bool f) { int i,j; if(!f) { for(i=1;i<=n;i++) { lx[i]=-0x7FFFFFFF; ly[i]=0; for(j=1;j<=n;j++) { weight[i][j]=-weight[i][j]; lx[i]=max(lx[i],weight[i][j]); } } } else { for(i=1;i<=n;i++) { lx[i]=0; ly[i]=0; for(j=1;j<=n;j++) lx[i]=max(lx[i],weight[i][j]); } } memset(match,-1,sizeof(match)); for(i=1;i<=n;i++) while(1) { memset(sx,0,sizeof(sx)); memset(sy,0,sizeof(sy)); for(j=0;j<=Maxn-1;j++) slack[j]=0x7FFFFFFF; if(dfs(i)) break; int dx=0x7FFFFFFF; for(j=1;j<=n;j++) dx=min(dx,slack[j]); for(j=1;j<=n;j++) { if(sx[j]) lx[j]-=dx; if(sy[j]) ly[j]+=dx; } } int ans=0; for(i=1;i<=n;i++) ans+=weight[match[i]][i]; if(!f) ans=-ans; return ans; } int main() { int i,j,N,M,a,b; char str[110]; while(scanf("%d%d",&N,&M),N||M) { a=b=0; for(i=1;i<=N;i++) { scanf("%s",&str); for(j=0;j<M;j++) { if(str[j]=='H') house[++a].x=i,house[a].y=j; if(str[j]=='m') man[++b].x=i,man[b].y=j; } } n=a; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) weight[i][j]=Dis(man[i],house[j]); } printf("%d\n",bestmatch(false)); } return 0; }