Description
On a grid map there are n little men and n houses. In each unit time, every little man can move one unit step, either horizontally, or vertically, to an adjacent point. For each little man, you need to pay a $1 travel fee for every step he moves, until he enters a house. The task is complicated with the restriction that each house can accommodate only one little man.
Your task is to compute the minimum amount of money you need to pay in order to send these n little men into those n different houses. The input is a map of the scenario, a '.' means an empty space, an 'H' represents a house on that point, and am 'm' indicates there is a little man on that point.

You can think of each point on the grid map as a quite large square, so it can hold n little men at the same time; also, it is okay if a little man steps on a grid with a house without entering that house.
Input
There are one or more test cases in the input. Each case starts with a line giving two integers N and M, where N is the number of rows of the map, and M is the number of columns. The rest of the input will be N lines describing the map. You may assume both N and M are between 2 and 100, inclusive. There will be the same number of 'H's and 'm's on the map; and there will be at most 100 houses. Input will terminate with 0 0 for N and M.
Output
For each test case, output one line with the single integer, which is the minimum amount, in dollars, you need to pay.
Sample Input
2 2
.m
H.
5 5
HH..m
.....
.....
.....
mm..H
7 8
...H....
...H....
...H....
mmmHmmmm
...H....
...H....
...H....
0 0
Sample Output
2
10
28
Source
题意:每个人都走到一个屋子的最小花费
关键是构图:

找到两个点作为源点和汇点,在源点和汇点之间有房子和人,假定左边的绿色的为house,右边的蓝色的为people,它们的数量分别为house和people,那么建立连通图。
其中从源点到house之间的边权为0,cap为1、从house到people之间的边权为x,cap为1(其中x为我们需要计算的两点之间的距离)、从people到汇点之间的边权为0,cap为1.
跑最小费用最大流即可。
SPFA费用流
#include<stdio.h>
#include<math.h>
#include<string>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<stack>
#include<queue>
#include<map>
#include<vector>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1e3+100;
const int maxm=1e6+100;
char mapp[maxn][maxn];
struct Edge{
int to,next,flow,cost,cap;
}edge[maxm];
struct E{
int x,y,cost;
}edges1[maxm],edges2[maxm];
int head[maxn],tol;
int pre[maxn],dis[maxn];
bool vis[maxn];//判断是否在队列里面
int N,n,m,house,man;
void debug(){
cout<<"_____five_____"<<endl;
}
void init()
{
house=0;
man=0;
tol=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost){
edge[tol].to=v,edge[tol].cap=cap,edge[tol].cost=cost,edge[tol].flow=0;
edge[tol].next=head[u],head[u]=tol++;
edge[tol].to=u,edge[tol].cap=0,edge[tol].cost=-cost,edge[tol].flow=0;
edge[tol].next=head[v],head[v]=tol++;
}
bool spfa(int s,int t){
queue<int>q;
for(int i=0;i<N;++i){
dis[i]=inf;
vis[i]=false;
pre[i]=-1;
}
dis[s]=0,vis[s]=true;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=false;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost)
{
dis[v]=dis[u]+edge[i].cost;
pre[v]=i;
if(!vis[v]){
vis[v]=true;
q.push(v);
}
}
}
}
if(pre[t]==-1) return false;
return true;
}
int minCostMaxflow(int s,int t,int &cost){
int flow=0;
cost=0;
while(spfa(s,t)){
int MIN=inf;
for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
MIN=min(MIN,edge[i].cap-edge[i].flow);
}
for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
edge[i].flow+=MIN;
edge[i^1].flow-=MIN;
cost+=edge[i].cost*MIN;
}
flow+=MIN;
}
return flow;
}
int main()
{
while(~scanf("%d%d",&n,&m)&&m+n){
init();
for(int i=0;i<n;++i)
scanf("%s",mapp[i]);
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
if(mapp[i][j]=='H'){
edges1[++house].x=i;
edges1[house].y=j;
}
else if(mapp[i][j]=='m'){
edges2[++man].x=i;
edges2[man].y=j;
}
}
}
for(int i=1;i<=man;++i){
addedge(0,i,1,0);
}
for(int i=1;i<=man;++i){
for(int j=1;j<=house;++j){
addedge(i,man+j,1,abs(edges1[i].x-edges2[j].x)+abs(edges1[i].y-edges2[j].y));
}
}
for(int i=1;i<=house;++i){
addedge(man+i,man+house+1,1,0);
}
N=house+man+2;
int cost;
int flow=minCostMaxflow(0,house+man+1,cost);
printf("%d\n",cost);
}
return 0;
}
因为这道题是二分图类型,使用ZKW费用流为更优选择:

#include<stdio.h>
#include<math.h>
#include<string>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<stack>
#include<queue>
#include<map>
#include<vector>
using namespace std;
const int maxn=1e3+10;
const int maxm=maxn*maxn;
const int inf=0x3f3f3f3f;
struct Edge{
int to,next,cap,flow,cost;
Edge(int _to = 0 ,int _next = 0,int _cap = 0,int _flow = 0,int _cost = 0):
to(_to),next(_next),cap(_cap),flow(_flow),cost(_cost){}
}edge[maxm];
struct E_edge{
int x,y;
}edge_house[maxm],edge_people[maxm];
int N,n,m;
int house,people;
char mapp[maxn];
struct ZKW_MinCostMaxFlow{
int head[maxn],tot;
int cur[maxn],dis[maxn];
bool vis[maxn];
int ss,tt,N;//源点,汇点和点的总个数
int min_cost,max_flow;
void init(){
house=people=0;
tot=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost){
edge[tot]=Edge(v,head[u],cap,0,cost);
head[u]=tot++;
edge[tot]=Edge(u,head[v],0,0,-cost);
head[v]=tot++;
}
int aug(int u,int flow){
if(u==tt) return flow;
vis[u]=true;
for(int i=cur[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(edge[i].cap>edge[i].flow&&!vis[v]&&dis[u]==dis[v]+edge[i].cost){
int tmp=aug(v,min(flow,edge[i].cap-edge[i].flow));
edge[i].flow+=tmp;
edge[i^1].flow-=tmp;
cur[u]=i;
if(tmp) return tmp;
}
}
return 0;
}
bool modify_label()
{
int d=inf;
for(int u=0;u<N;++u){
if(vis[u]){
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(edge[i].cap>edge[i].flow&&!vis[v]){
d=min(d,dis[v]+edge[i].cost-dis[u]);
}
}
}
}
if(d==inf) return false;
for(int i=0;i<N;++i){
if(vis[i]){
vis[i]=false;
dis[i]+=d;
}
}
return true;
}
pair<int,int>mincostmaxflow(int start,int end,int n){
ss=start,tt=end,N=n;
min_cost=max_flow=0;
for(int i=0;i<n;++i) dis[i]=0;
while(1){
for(int i=0;i<n;++i){
cur[i]=head[i];
}
while(1){
for(int i=0;i<n;++i) vis[i]=false;
int tmp=aug(ss,inf);
if(tmp==0) break;
max_flow+=tmp;
min_cost+=tmp*dis[ss];
}
if(!modify_label()) break;
}
return make_pair(min_cost,max_flow);
}
}solve;
int main()
{
while(~scanf("%d%d",&n,&m)&&n+m){
solve.init();
for(int i=0;i<n;++i){
scanf("%s",mapp);
for(int j=0;j<m;++j){
//现在判断的是H
if(mapp[j]=='H'){
edge_house[++house].x=i;
edge_house[house].y=j;
}
}
for(int j=0;j<m;++j){
if(mapp[j]=='m'){
edge_people[++people].x=i;
edge_people[people].y=j;
}
}
}
//使用结构体存图
for(int i=1;i<=house;++i){
solve.addedge(0,i,1,0);
}
for(int i=1;i<=house;++i){
for(int j=1;j<=people;++j){
int l=abs(edge_people[i].x-edge_house[j].x)+abs(edge_people[i].y-edge_house[j].y);
solve.addedge(i,house+j,1,l);
}
}
for(int i=1;i<=people;++i){
solve.addedge(house+i,house+people+1,1,0);
}
N=house+people+2;
//顶点总数N的确定,是否包含源点
pair<int,int>ans=solve.mincostmaxflow(0,house+people+1,N);
printf("%d\n",ans.first);
}
return 0;
}
POJ 2195&spm=1001.2101.3001.5002&articleId=103885079&d=1&t=3&u=a39dce04c1e84afba628eb2359cf938a)

被折叠的 条评论
为什么被折叠?



