最小费用最大流(spfa、EK算法及(构图----尤其重要))

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string.h>
#include <queue>
#include <vector>           //poj2195最小费用最大流(spfa、EK算法及(构图----尤其重要))
#define inf 1<<30                      
#define N 210
using namespace std;  //构图时,要建立超级源点和超级汇点,源点u都会和每个人相连,cost[u][x]=0, cap[u][x]=1, 每个房子都会和汇点e相连,cost[x][e]=0, cap[x][e]=1
int mantot, houtot, p[N], dis[N], vis[N], cap[N][N], cost[N][N]; //cap表示容量, cost表示花费, 最小费用相当于最短路,最大流相当于每个人对应于一个房子
queue<int> q;
struct node
{
	int x, y;
}man[N], hou[N];
int fab(int x)
{
	if(x>=0)return x;
	else  return -x;
}
int step(int a, int b)
{
	return fab(man[a].x-hou[b].x)+fab(man[a].y-hou[b].y);
}
void buildmap()
{
	int t, j;
	for(t=0; t<mantot; ++t)  //建立超级源点0, 超级汇点1,其他节点从2开始
	{
		cap[0][t+2]=1;
		cost[0][t+2]=0;
	}
	for(t=0; t<houtot; ++t)  //建立超级汇点
	{
		cap[t+2+mantot][1]=1;
		cost[t+2+mantot][1]=0;
	}
	for(j=0; j<mantot; j++)    //建立人和房子之间的距离,连接起来
	{
		for(t=0; t<houtot; ++t)
		{
			cap[j+2][t+2+mantot]=1;
			cost[j+2][t+2+mantot]=step(j, t);
			cost[t+2+mantot][j+2]=-cost[j+2][t+2+mantot];//注意逆边一定要记得加
		}
	}
	return ;
}
int mpmf()
{
	int s=0, e=1, t, g=mantot+houtot+2, u, f=0, totprice=0, flow;
	while(!q.empty())
		q.pop();
	while(1)
	{
		for(t=0; t<g; ++t)
		{
			dis[t]=inf;
			vis[t]=0;
		}
		dis[s]=0;
		q.push(s);
		vis[s]=1;
		while(!q.empty())     //spfa算法
		{
			u=q.front();
			q.pop();
			vis[u]=0;
			for(t=0; t<g; ++t)
			{
				if(cap[u][t]&&dis[t]>dis[u]+cost[u][t])
				{
					dis[t]=dis[u]+cost[u][t];
					p[t]=u;
					if(!vis[t])
					{
						vis[t]=1;
						q.push(t);
					}
				}
			}
		}
		if(dis[1]==inf)   //已经达到最大流且是最小费用
			break;
		for(t=1, flow=inf; t!=0; t=p[t])  //寻找最小流量
		{
			if(cap[p[t]][t]<flow)
				flow=cap[p[t]][t];
		}
		for(t=1; t!=0; t=p[t])
		{
			cap[p[t]][t]-=flow;
			cap[t][p[t]]+=flow;
		}
		f+=flow;
		totprice+=flow*dis[1];
	}
	return totprice;
}

int main()
{
	int n, m, t, j;
	char a[N][N];
	while(scanf("%d%d", &n, &m)!=EOF)
	{
		if(n==0||m==0)break;
		for(t=0; t<n; ++t)
			scanf("%s", &a[t]);
		mantot=houtot=0;    //人的总数,房子的总数
		memset(cap, 0, sizeof(cap));//注意!!!  初始化,0表示没有办法到达
		for(j=0; j<n; ++j)
		{
			for(t=0; t<m; ++t)
			{
				if(a[j][t]=='H')
				{
					hou[houtot].x=j;
					hou[houtot++].y=t;
				}
				else if(a[j][t]=='m')
				{
					man[mantot].x=j;
					man[mantot++].y=t;
				}
			}
		}
		buildmap();  //建图
		printf("%d\n", mpmf());  //minpricemaxflow最小费用最大流
	}
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值