您的位置:首页 > 编程语言 > Go语言

POJ - 2195 Going Home(最小费用最大流)

2015-11-25 21:05 513 查看
1、N*M的矩阵中,有k个人和k个房子,每个人分别进入一个房子中,求所有人移动的最小距离。

2、人看成源点,房子看成汇点,求最小费用最大流。

建图--

人指向房子,容量为1,费用为人到房子的曼哈顿距离。

建立超级源点和超级汇点:超级源点指向人,容量为1,费用为0;房子指向超级汇点,容量为1,费用为0。

求超级源点到超级汇点的最小费用最大流即可。

ps:容量为什么都设为1?---有待研究。。

今天又看了下,设为1是必须的。

超级源点指向人,容量为1,保证人这个点只有一个人;

房子指向超级汇点,容量为1,保证一个房子只能容纳一个人。

然而,人指向房子,容量为1,这并不是必须的,因为前两个条件已经足够了。为了简单,这里仍然把这个容量设为1。

3、

1、Bellman-Ford:

/*
zkw费用流
对于二分图类型的比较高效
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

const int MAXN=256;
const int MAXM=20500;
const int INF=0x3f3f3f3f;
struct Edge{
int to,next,cap,flow,cost;
Edge(int _to=0,int _next=0,int _cap=0,int _flow=0,int _cost=0):
to(_to),next(_next),cap(_cap),flow(_flow),cost(_cost){}
}edge[MAXM];
struct ZKW_MinCostMaxFlow{
int head[MAXN],tot;
int cur[MAXN];
int dis[MAXN];
bool vis[MAXN];
int ss,tt,N;//源点、汇点和点的总个数(编号是0~N-1),不需要额外赋值,调用会直接赋值
int min_cost,max_flow;
void init(){
tot=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost){
edge[tot]=Edge(v,head[u],cap,0,cost);
head[u]=tot++;
edge[tot]=Edge(u,head[v],0,0,-cost);
head[v]=tot++;
}
int aug(int u,int flow){
if(u==tt)return flow;
vis[u]=true;
for(int i=cur[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(edge[i].cap>edge[i].flow&&!vis[v]&&dis[u]==dis[v]+edge[i].cost){
int tmp=aug(v,min(flow,edge[i].cap-edge[i].flow));
edge[i].flow+=tmp;
edge[i^1].flow-=tmp;
cur[u]=i;
if(tmp)return tmp;
}
}
return 0;
}
bool modify_label(){
int d=INF;
for(int u=0;u<N;u++)
if(vis[u])
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(edge[i].cap>edge[i].flow&&!vis[v])
d=min(d,dis[v]+edge[i].cost-dis[u]);
}
if(d==INF)return false;
for(int i=0;i<N;i++)
if(vis[i]){
vis[i]=false;
dis[i]+=d;
}
return true;
}
/*
直接调用获取最小费用和最大流
输入:start-源点,end-汇点,n-点的总个数(编号从0开始)
返回值:pair<int,int>第一个是最小费用,第二个是最大流
*/
pair<int,int> mincostmaxflow(int start,int end,int n){
ss=start,tt=end,N=n;
min_cost=max_flow=0;
for(int i=0;i<n;i++)dis[i]=0;
while(1){
for(int i=0;i<n;i++)cur[i]=head[i];
while(1){
for(int i=0;i<n;i++)vis[i]=false;
int tmp=aug(ss,INF);
if(tmp==0)break;
max_flow+=tmp;
min_cost+=tmp*dis[ss];
}
if(!modify_label())break;
}
return make_pair(min_cost,max_flow);
}
}solve;

struct point{//图中的一个点
int x;
int y;
};
point man[128];//存储人
point house[128];//存储房子
int man_sum;//人的总数
int house_sum;//房子的总数

int main(){
int N,M;//N*M的矩阵
char str[128];
int S,T;//超级源点,超级汇点
//int mincost;
//int maxflow;
pair<int,int>pr;//最小花费,最大流

while(~scanf("%d%d",&N,&M)){
if(N==0&&M==0)break;
man_sum=0;
house_sum=0;

for(int i=0;i<N;++i){
scanf("%s",str);
for(int j=0;j<M;++j){
if(str[j]=='m'){
man[man_sum].x=i;
man[man_sum++].y=j;
}
else if(str[j]=='H'){
house[house_sum].x=i;
house[house_sum++].y=j;
}
}
}

solve.init();
for(int i=0;i<man_sum;++i){
for(int j=0;j<house_sum;++j){
solve.addedge(i,man_sum+j,1,abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y));
}
}
//超级源点
S=man_sum+house_sum;
for(int i=0;i<man_sum;++i){
solve.addedge(S,i,1,0);
}
//超级汇点
T=man_sum+house_sum+1;
for(int i=0;i<house_sum;++i){
solve.addedge(man_sum+i,T,1,0);
}

pr=solve.mincostmaxflow(S,T,man_sum+house_sum+2);
printf("%d\n",pr.first);
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: