1. 程式人生 > >Going Home (費用流)

Going Home (費用流)

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

題意:

給定一個N*M的地圖,地圖上有若干個人和房間,且人與房間的數量一致。人每移動一格需花費$1(即單位費用=單位距離),一間房間只能入住一個人現在要求所有的人都入住房間,求最小費用。

思路:

最小費用最大流模板題,關鍵在於建模,且建模如下:

源點s編號0, 人編號1到n, 房子編號n+1到n+n, 匯點編號t.

源點s到每個人i有邊(s, i, 1,0)

每個人i到每個房子j有邊(i, j, 1, i人到j房的開銷)

每個房子j到匯點t有邊(j, t, 1, 0)

最終我們求出的最小費用就是所求.

程式碼:

#include<map>
#include<vector>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define maxn 200+5
#define PI acos(-1.0)
#define INF 1e9  
using namespace std;
typedef long long ll;

struct Edge{
	int from,to,cap,flow,cost;
	Edge(){}
	Edge(int f,int t,int c,int fl,int co):from(f),to(t),cap(c),flow(fl),cost(co){}
}; 

struct MCMF{
	int n,m,s,t;
	vector<Edge>edges;
	vector<int>G[maxn];
	int inq[maxn];
	int d[maxn];
	int p[maxn];
	int a[maxn];
	
	
	void init(int n,int s,int t){
		this->n=n;
		this->s=s;
		this->t=t;
		for(int i=0;i<n;i++){
			G[i].clear();
		}
		edges.clear();
	}
	
	void AddEdge(int from,int to,int cap,int cost){
		edges.push_back((Edge){from,to,cap,0,cost});
		edges.push_back((Edge){to,from,0,0,-cost}) ;
		m=edges.size();
		G[from].push_back(m-2);
		G[to].push_back(m-1);
	}
	
	bool BellmanFord(int &flow,int &cost){
		for(int i=0;i<n;i++) d[i]=INF;
		memset(inq,0,sizeof(inq));
		d[s]=0; inq[s]=1; p[s]=0; a[s]=INF;
		queue<int>Q;
		Q.push(s);
		while(!Q.empty()){
			int u=Q.front();
			Q.pop();
			inq[u] =0;
			for(int i=0;i<G[u].size();i++){
				Edge& e=edges[G[u][i]];
				if(e.cap>e.flow &&d[e.to]>d[u]+e.cost){
					d[e.to]=d[u]+e.cost;
					p[e.to]=G[u][i];
					a[e.to]=min(a[u],e.cap-e.flow);
					if(!inq[e.to]) {
						Q.push(e.to);
						inq[e.to]=1;
					}
				}
			}	
		}
		if(d[t]==INF) return false;
		flow+=a[t];
		cost+=d[t]*a[t];
		int u=t;
		while(u!=s) {
			edges[p[u]].flow+=a[t];
			edges[p[u]^1].flow-=a[t];
			u=edges[p[u]].from;
		}
		return true;
	}
	
	int Mincost(){
		int flow=0;
		int cost=0;
		while(BellmanFord(flow,cost));
		return cost;
	}
}MM;

struct Node{
	int x,y;
	Node(){}
	Node(int x,int y):x(x),y(y) {}
	
	int dis(Node &b){
		
		return abs(x-b.x)+abs(y-b.y);
		
	}
}node1[maxn],node2[maxn]; 

int main()
{
	int n,m;
	while(cin>>n>>m &&n){
		int num1=0,num2=0;
		for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			char ch;
			cin>>ch;
			if(ch=='m')  node1[num1++]=Node(i,j);
			else if(ch=='H') node2[num2++]=Node(i,j);
		}
		int src=0,dst=num1*2+1;
		MM.init(num1*2+2,src,dst) ;
		for(int i=1;i<=num1;i++){
			MM.AddEdge(src,i,1,0) ;
			MM.AddEdge(num1+i,dst,1,0);
			for(int j=1;j<=num1; j++)
			MM.AddEdge(i,num1+j,1,node1[i-1].dis(node2[j-1])) ;
			
		}
		printf("%d\n",MM.Mincost());
	}
    return 0;
}