原题地址:点击打开链接
建立源点s,汇点t,s与每个人之间建边,费用为0,容量为1,每个房子与t建边,费用为0容量为1,每个人与每个房子之间建边,费用为人到达房子的距离,容量为1,
则该题转化为求最小费用流问题。
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<vector>
#define MAX 1<<31-1
#define min(x,y)(x<y?x:y)
using namespace std;
char str[110][110];
struct Point
{
int x;
int y;
}home[110],people[110];
struct Edge
{
int to;
int cost;
int cap;
int rev;
};
vector<Edge>g[210];
int dis[210],pree[210],prev[210];
int dist(Point p1,Point p2)
{
int a=p1.y-p2.y;
int b=p1.x-p2.x;
if(a<0)
a=-a;
if(b<0)
b=-b;
return a+b;
}
void add_edge(int from,int to,int cap,int cost)
{
g[from].push_back((Edge){to,cost,cap,g[to].size()});
g[to].push_back((Edge){from,-cost,0,g[from].size()-1});
}
int min_cost_flow(int s,int t,int f)
{
bool update;
int res=0,u,v,i;
while(f>0)
{
update=true;
for(i=0;i<=t;i++)
dis[i]=MAX;
dis[s]=0;
while(update)
{
update=false;
for(v=0;v<=t;v++)
{
for(i=0;i<g[v].size();i++)
{
Edge e=g[v][i];
if(e.cap>0&&dis[e.to]>dis[v]+e.cost)
{
dis[e.to]=dis[v]+e.cost;
pree[e.to]=i;
prev[e.to]=v;
update=true;
}
}
}
}
if(dis[t]==MAX)
break;
int d=MAX;
for(u=t;u!=s;u=prev[u])
{
d=min(d,g[prev[u]][pree[u]].cap);
}
res+=dis[t];
f-=d;
for(u=t;u!=s;u=prev[u])
{
Edge &e=g[prev[u]][pree[u]];
e.cap-=d;
g[e.to][e.rev].cap+=d;
}
}
return res;
}
int main()
{
int m,n,i,j,num1,num2,s,t;
while(scanf("%d%d",&m,&n)&&(m||n))
{
num1=0;
num2=0;
gets(str[0]);
for(i=0;i<m;i++)
gets(str[i]);
for(i=0;i<m;i++)
for(j=0;j<n;j++)
{
if(str[i][j]=='m')
{
people[++num1].x=i;
people[num1].y=j;
}
else if(str[i][j]=='H')
{
home[++num2].x=i;
home[num2].y=j;
}
}
s=0;
t=num1+num2+1;
for(i=s;i<=t;i++)
g[i].clear();
for(i=1;i<=num1;i++)
{
add_edge(s,i,1,0);
}
for(i=1;i<=num2;i++)
{
add_edge(num1+i,t,1,0);
}
for(i=1;i<=num1;i++)
for(j=1;j<=num2;j++)
{
int d=dist(people[i],home[j]);
add_edge(i,num1+j,1,d);
}
int res=min_cost_flow(s,t,num1);
printf("%d\n",res);
}
return 0;
}