poj 2195 Going Home(最小费用流)

题目链接:http://poj.org/problem?id=2195

题目大意是给一张网格,网格中m代表人,h代表房子,网格中的房子和人数量相等,人可以向上向下走,每走1步花费加1,每个房子只能住一个人,问使得所有人住房子里最少的花费是多少?

最小费用流的题目,最大流跑spfa,每个人和每个房子之间建边,边的流量为1,花费是人到房子的距离,假定一个源点和一个汇点,源点连接所有的人,每条边流量为1,花费为0,所有的房子连接汇点,同样的边流量是1,花费是0,从源点到汇点跑一下费用流就可以得出答案。其中每个人到每个房子的距离d = 纵坐标的差值绝对值+横坐标的差值绝对值。图很好建,细节注意一下,多组数据记得初始化。

AC代码:

#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;
#define inf 0x3f3f3f3f
struct node{
	vector<int> vex;//g[i]连接的点集 
	vector<int> num;//g[i]连接的边集 
}g[205];
struct edge{
	int u,v,c,cost;//u起点,v终点,c流量 cost花费 
}e[205*205];
vector<pair<int,int> > Vmen;//存储人的坐标 
vector<pair<int,int> > Vh;//存储房子的坐标 
int men = 0,h = 0;//人和房子的个数 
int sp,tp;//源点汇点 
int inq[205],d[205],pre[205];
int cal(int a,int b){// a为人,b为房子 first横坐标,second纵坐标 
	return abs(Vmen[a].first - Vh[b].first)+abs(Vmen[a].second - Vh[b].second);
}
int edgenum ;//边的序号从0开始,
void addedge(int u,int v,int c,int cost){
	e[edgenum].u = u,e[edgenum].v = v;
	e[edgenum].c = c,e[edgenum].cost = cost;
	g[u].vex.push_back(v),g[u].num.push_back(edgenum++); 
	// 建立双向边操作 
	e[edgenum].u = v,e[edgenum].v = u;
	e[edgenum].c = 0,e[edgenum].cost = -cost;
	g[v].num.push_back(edgenum++),g[v].vex.push_back(u);   
}
bool spfa(int s,int t){
	for(int i = 0;i<205;i++){
		inq[i] = 0;//是否在队列 
		d[i] = inf;//记录最小花费 
		pre[i] = -1;//初始化-1 
	}//初始化 
	d[s] = 0,inq[s] = 1;
	queue<int> q;
	q.push(s);
	while(!q.empty()){
		int now = q.front() ;
		q.pop() ;
		inq[now] = 0;
		for(int i = 0;i<g[now].num.size() ;i++ ){
			int te = g[now].num[i];
			int tv = g[now].vex[i]; 
			if(e[te].c >0 && d[tv] > d[now] + e[te].cost ){
				d[tv] = d[now] + e[te].cost ;
				pre[tv] = te;//存储以tv为后继节点的边集 该边te : now->tv 
				if(!inq[tv]){
					inq[tv] = 1;
					q.push(tv); 
				} 
			}
		} 
	}
	return d[tp]!=inf; //找不到增广路就返回false 
}
int mincostmaxflow(int s,int t){
	int ans_cost = 0, u,minCut;
	while(spfa(s,t)){
		minCut = inf;
		for(u = pre[t];u!=-1;u = pre[e[u].u]){
			if(minCut > e[u].c ){
				minCut = e[u].c ;
			}
		}
		for(u = pre[t];u!=-1;u = pre[e[u].u]){
			e[u^1].c+=minCut;//双向边做加和该增广路最大流操作 
			e[u].c-=minCut;  
		}
		ans_cost+=minCut*d[tp];
	}
	return ans_cost;
}
vector<string> map;
int main(){
	int n,m;
	while(cin>>n>>m){
		if(!n&&!m){
			return 0;
		}
		edgenum = 0,map.clear(),Vmen.clear(),Vh.clear(),men = 0,h = 0;//初始化数据 
		for(int i = 0;i<205;i++){
			g[i].num.clear();
			g[i].vex.clear();  
		}//初始化数据 
		for(int i = 0;i<n;i++){
		string t;
		cin>>t;
		map.push_back(t);			
		}
		for(int i = 0;i<map.size();i++){
			for(int j = 0;j<m;j++){
				if(map[i][j] == 'H'){
					h++;
					Vh.push_back(make_pair(i,j)); 
				}
				if(map[i][j] == 'm'){
					men++;
					Vmen.push_back(make_pair(i,j)); 
				}
			}
		}
		sp = 0;
		tp = men+h+1;
		for(int i = 1;i<=men;i++){
			addedge(sp,i,1,0);
		}
		for(int i = men+1;i<=2*men;i++){
			addedge(i,tp,1,0);
		}   
		for(int i = 0;i<Vmen.size() ;i++){
			for(int j = 0;j<Vh.size() ;j++){
				int cost = cal(i,j);
				addedge(i+1,men+j+1,1,cost);
			}
		}
		cout<<mincostmaxflow(sp,tp)<<endl; 
	}
	return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值