[题目大意]:给出n*m的地图,由几个H(房子)和m(人),求每个人都走到一个房子的最少需要的总步数。
n,m<=100 ; H==m ;
两种方法,看心情!
1.最小费用最大流
算出每个人到每个房子的步数;
s和每个人连边,容量1,费用0
每个人和每个房子连边,容量inf, 费用为步数
每个房子和t连边,容量1,费用0 ;
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N= 20000 ;
const int Max= 100000 ;
const int inf=1<<30 ;
struct node
{
int u ,v,c,cost,next ;
}edge[Max];
struct node1
{
int x,y ;
}M[N],H[N] ;
int head[N],vist[N],dist[N],pp[N],pre[N] ;
char g[N][N] ;
int top ;
void add(int u ,int v,int c,int cost)
{
edge[top].u=u;
edge[top].v=v;
edge[top].c=c;
edge[top].cost=cost;
edge[top].next=head[u];
head[u]=top++;
edge[top].u=v;
edge[top].v=u;
edge[top].c=0;
edge[top].cost=-cost;
edge[top].next=head[v];
head[v]=top++;
}
int SPFA(int s,int t)
{
int u , v ;
memset(vist,0,sizeof(vist));
memset(pre,-1,sizeof(pre));
for(int i = 0 ; i <= t ; i++) dist[i]=inf ;
vist[s]=1;dist[s]=0;pre[s]=s;
queue<int>q;
q.push(s);
while(!q.empty())
{
u=q.front();
q.pop();
vist[u]=0;
for(int i =head[u];i!=-1;i=edge[i].next)
{
v=edge[i].v;
if(edge[i].c && dist[v] > dist[u]+edge[i].cost)
{
dist[v] = dist[u]+edge[i].cost ;
pre[v]=u;
pp[v]=i;
if(!vist[v]);
{
vist[v]=1;
q.push(v);
}
}
}
}
if(dist[t]==inf) return 0;
return 1 ;
}
int MFMC(int s,int t)
{
int mincost=0,flow=0,minflow ;
while(SPFA(s,t))
{
minflow=inf;
for(int i=t;i!=s;i=pre[i])
minflow=min(minflow,edge[pp[i]].c);
for(int i=t;i!=s;i=pre[i])
{
edge[pp[i]].c -= minflow;
edge[pp[i]^1].c += minflow;
}
flow += minflow;
mincost += dist[t]*minflow ;
// printf("****");
}
return mincost ;
}
int main()
{
int n,m ;
while(~scanf("%d%d",&n,&m))
{
if((n+m)==0) break;
top = 0 ;
memset(head,-1,sizeof(head)) ;
for(int i = 0 ; i < n ; i++)
scanf("%s",g[i]) ;
int k=0,k1=0;
for(int i = 0 ; i < n ; i++)
for(int j = 0 ; j < m ; j++)
{
if(g[i][j]=='m') //人
M[k].x=i,M[k++].y=j ;
if(g[i][j]=='H') //房子
H[k1].x=i,H[k1++].y=j;
}
for(int i = 0 ; i < k ; i++)
for(int j = 0 ; j < k1 ; j++)
{ //人到房子的步数
int w=abs(M[i].x-H[j].x) + abs(M[i].y-H[j].y) ;
add(i+1,j+k+1,inf,w) ;
} //k其实等于k1,房子和人的数量相等
int s = 0 , t = k+k1+1 ;
for(int i = 0 ; i < k ; i++)
add(s,i+1,1,0);
for(int i = 0 ; i <k1 ; i++)
add(i+k+1,t,1,0) ;
int ans = MFMC(s,t) ;
printf("%d\n",ans) ;
}
return 0 ;
}
2. 最小权匹配
每个人和房子连边,权值为步数的相反数 ,也就是负的 ;
求一遍最大权匹配, 再把答案变回正数 ;
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N= 200 ;
const int inf=1<<30 ;
struct node1
{
int x,y ;
}M[N],H[N] ;
int f[N][N] ,lx[N],ly[N],fx[N],fy[N],match[N] ;
char g[N][N] ;
int k,k1;
int dfs(int u)
{
fx[u]=1;
for(int i = 1 ; i <= k1 ; i++)
{
if(!fy[i] && lx[u]+ly[i] == f[u][i])
{
fy[i]=1;
if(match[i]==-1 || dfs(match[i]))
{
match[i]=u;
return 1 ;
}
}
}
return 0;
}
int KM()
{
memset((ly),0,sizeof(ly)) ;
memset(match,-1,sizeof(match)) ;
for(int i = 1 ; i <= k ;i++)
{
lx[i]=-inf ;
for(int j = 1 ; j<=k1 ; j++ )
lx[i] = max(lx[i],f[i][j]) ;
}
for(int kk = 1 ; kk <= k ; kk++ )
{
while(1)
{
memset(fx,0,sizeof(fx)) ;
memset(fy,0,sizeof(fy)) ;
if(dfs(kk)) break;
int d = inf ;
for(int i = 1 ; i <= k ; i++)
if(fx[i])
for(int j = 1 ; j <=k1 ;j++)
if(!fy[j])
d = min( d,lx[i]+ly[j]-f[i][j]) ;
for(int i = 1 ; i <= k ; i++) if(fx[i]) lx[i] -= d ;
for(int i = 1 ; i <= k1 ; i++ ) if(fy[i]) ly[i] += d ;
}
}
int ans = 0 ;
for(int i = 1 ;i <= k ; i++)
ans += f[match[i]][i] ;
return -ans ;
}
int main()
{
int n,m ;
while(~scanf("%d%d",&n,&m))
{
if((n+m)==0) break;
memset(f,0,sizeof(f)) ;
for(int i = 0 ; i < n ; i++)
scanf("%s",g[i]) ;
k=0,k1=0;
for(int i = 0 ; i < n ; i++)
for(int j = 0 ; j < m ; j++)
{
if(g[i][j]=='m')
M[k].x=i,M[k++].y=j ;
if(g[i][j]=='H')
H[k1].x=i,H[k1++].y=j;
}
for(int i = 0 ; i < k ; i++)
for(int j = 0 ; j < k1 ; j++)
{
int w=abs(M[i].x-H[j].x) + abs(M[i].y-H[j].y) ;
f[i+1][j+1]=-w;
}
printf("%d\n",KM()) ;
}
return 0 ;
}