poj 2195 Going Home

/*

题意:一个n×m的矩阵,m表示人,H表示房子,.表示空地,人数和房子数相等,如下图:
5 5
HH..m
.....
.....
.....
mm..H
现在要让所有的人都进入不同的房子内,人只能横着或竖着走一格,问总共最少走多少步?

*/
#include <iostream> // 最小费用最大流,基于邻接矩阵
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int INF = 1<<30;
const int MAXN = 210;

int s, t; //s,t分别是超级源点和汇点
int d[MAXN], p[MAXN], cap[MAXN][MAXN], cost[MAXN][MAXN], flow[MAXN][MAXN];
//根据题意,图中并不存在平行边和反向边,故可以用邻接矩阵cap和cosst保存各边的容量和费用
void build(int n)
{
int house[MAXN][2],man[MAXN][2];
int h_len=0,m_len=0;
char ch[102];
memset(cap, 0, sizeof(cap));
memset(cost, 0, sizeof(cost));
for(int i=0;i<n;++i)
{
scanf("%s",ch);
for(int j=0;j<strlen(ch);++j)
{
if(ch[j]=='m')
{
int& r=m_len;
r++;
man[r][0]=i;
man[r][1]=j;
}
else if(ch[j]=='H')
{
int& r=h_len;
r++;
house[r][0]=i;
house[r][1]=j;
}
}
}
//因为人数和房子数相等,所以m_len和h_len相等
//人分布在[1,m_len],房子分布在[m_len+1,m_len+h_len],超级源点和汇点是 0 , m_len+h_len+1
int from,to;
for(int i=1;i<=m_len;++i)
{
for(int j=1;j<=h_len;++j)
{
from=i;
to=m_len+j;
cap[from][to]=1;
cost[from][to]=abs(man[i][0]-house[j][0])+abs(man[i][1]-house[j][1]);
cap[to][from]=0;
cost[to][from]=-cost[from][to];
}
}
s=0; //超级源点s
for(int i=1;i<=m_len;++i)
cap[s][i]=1;
t=m_len+h_len+1; //超级汇点t
for(int i=1;i<=h_len;++i)
cap[i+m_len][t]=1;
}
void ek()
{
int f = 0, c = 0; //f,c分别保存最大流流量和该流量下的最小费用

queue<int> q;
int inq[MAXN];
memset(flow, 0, sizeof(flow));
while(1)
{
for(int i = s; i <= t; i++) //s,t刚好是所有顶点的首尾,顶点下标范围[s,t]
d[i] = (i==s ? 0 : INF);
memset(inq, 0, sizeof(inq));
q.push(s);
inq[s]=1;
while(!q.empty())
{
int u = q.front(); q.pop();
inq[u] = 0;
for(int v = s; v <= t; v++) //注意顶点下标范围
{
if(cap[u][v]>flow[u][v] && d[v]>d[u]+cost[u][v])
{
d[v] = d[u] + cost[u][v];
p[v] = u;
if(!inq[v])
{
inq[v] = 1;
q.push(v);
}
}
}
}
if(d[t] == INF) break; //汇点不可达,表明当前流已经是最小费用最大流
int a = INF;
for(int u = t; u != s; u = p[u])
a =min(a, cap[p[u]][u] - flow[p[u]][u]); //计算可改进量
for(int u = t; u != s; u = p[u]) //增广
{
flow[p[u]][u] += a;
flow[u][p[u]] -= a;
}
c += d[t]*a; //更新总费用和流量
//f += a;
}
printf("%d\n", c); //输出总费用
}
int main()
{
int n,m;
while(scanf("%d%d", &n, &m)&&n) //n×m矩阵
{
build(n); //n并不是顶点数
ek();
}
return 0;
}

posted on 2012-03-31 10:03  sysu_mjc  阅读(335)  评论(0编辑  收藏  举报

导航