Going Home

点击打开链接

题意:在一张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
*/	
    
    
    
    


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值