题意:在一张N * M 的图中,有 n个房子和n个人。问最少使用多少总步数让每个房子都有且仅有一个人.
解析:构图。选取源点和汇点,从源点到人连通,费用为0(因为题目要求的是人到房子的费用)。以第i个人与第j所房子的距离作为当前费用进行构图,由于题目并不需要求出最大流( 也没给出来),因此所有连通路径的流量设置为一个相同的权值即可。
然后使用费用流求出最小费用。
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;
//最小费用最大流,求最大费用只需要取相反数,即结果取反即可;点数为N,编号从0~N - 1
const int maxn = 10000;
const int maxm = 100000;
const int INF = 0xfffffff;
struct Edge{
int to, next, cap, flow, cost;
}edge[ maxm ];
int head[ maxn ], tol;
int pre[ maxn ], dis[ maxn ];
bool vis[ maxn ];
int N;//节点个数,编号从0~N-1
void init( int n ){
N = n;
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;
else
return true;
}
//返回值:flow为最大流;cost为最小费用
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 ] ){
if( Min > edge[ i ].cap - edge[ i ].flow )
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 cost;
}
struct node{
int x, y;
}Home[ 205 ], Man[ 205 ];
char mapp[ 205 ][ 205 ];
int main(){
int n, m;
while( scanf( "%d%d", &n, &m ) != EOF ){
if( !n && !m )
break;
int k1 = 0, k2 = 0;
for( int i = 0; i < n; ++i ){
scanf( "%s", &mapp[ i ] );
for( int j = 0; j < m; ++j ){
//k1++;
if( mapp[ i ][ j ] == 'H' ){
k1++;
Home[ k1 ].x = i;
Home[ k1 ].y = j;
}
else if( mapp[ i ][ j ] == 'm' ){
k2++;
Man[ k2 ].x = i;
Man[ k2 ].y = j;
}
}
}
//cout << k1 << " " << k2 << endl;
int start = 0, end = k1 + k2 + 1, N = k1 + k2 + 2;
init( N );
for( int i = 1; i <= k1; ++i ){
addedge( 0, i, 1, 0 );
}
for( int i = 1; i <= k2; ++i ){
addedge( k1 + i, end, 1, 0 );
}
for( int i = 1; i <= k1; ++i ){
for( int j = 1; j <= k2; ++j ){
int temp = abs( Home[ i ].x - Man[ j ].x ) + abs( Home[ i ].y - Man[ j ].y );
//cout << temp << endl;
addedge( i, k1 + j, 1, temp );
}
}
int c;
int ans = minCostMaxflow( start, end, c );
cout << ans << endl;
}
return 0;
}
/*
2 2
.m
H.
5 5
HH..m
.....
.....
.....
mm..H
7 8
...H....
...H....
...H....
mmmHmmmm
...H....
...H....
...H....
2
10
28
*/