码迷,mamicode.com
首页 > 其他好文 > 详细

Going Home

时间:2014-07-21 13:38:37      阅读:226      评论:0      收藏:0      [点我收藏+]

标签:最小费用最大流

点击打开链接

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


Going Home

标签:最小费用最大流

原文地址:http://blog.csdn.net/bo_jwolf/article/details/38014417

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!