Problem Description
输入n,m,给你一个n*m的矩阵m代表人,H代表房子,每个人得进去一个房子,被进去的房子不能在进。人移动一单位距离需要花费1,问你最少花费
思路:
建图,求最大流就好了,找出所有的人的坐标,找出所有的房子的坐标,每个人到所有房子建立一条边,流量为1,费用为距离。超级源点到人建边,流量为1,费用0。房子到超级汇点建边,流量1,费用0。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
struct node
{
int u, to, w, val, next;//当前点,目的点,流量,费用,另外的目的点对应Map[]下标
};
node Map[100000];
char s[150][150];
int head[20000], dist[20000], vis[20000], pre[20000], cnt, flow[20000];
void add(int u, int v, int w, int val)//前向星存图
{
Map[cnt].u = u;
Map[cnt].to = v;
Map[cnt].w = w;//正向,流量w
Map[cnt].val = val;//正向,费用为正
Map[cnt].next = head[u];
head[u] = cnt++;
Map[cnt].u = v;
Map[cnt].to = u;
Map[cnt].w = 0;//负向,流量为0
Map[cnt].val = -val;//负向,费用为负
Map[cnt].next = head[v];
head[v] = cnt++;
}
int value(node x, node y)//求费用
{
return abs(x.u - y.u) + abs(x.to - y.to);
}
bool spfa(int s, int e)//求所有增广路中,最短路 && 记录路径
{
int i;
memset(pre, -1, sizeof(pre));//初始化
memset(dist, 0x3f3f3f3f, sizeof(dist));
memset(vis, 0, sizeof(vis));
memset(flow, 0, sizeof(flow));
queue<int>q;
q.push(s);
dist[s]=0;
flow[s] = 0x3f3f3f3f;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(i = head[u]; ~i; i = Map[i].next)
{
int to = Map[i].to, w = Map[i].w, val = Map[i].val;
if(w > 0 && dist[to] > dist[u] + val)
{
dist[to] = dist[u] + val;
pre[to] = i;//记录路径
flow[to] = min(flow[u], w);//求限制流量
if(!vis[to])
{
vis[to]=1;
q.push(to);
}
}
}
}
if(pre[e]==-1)return 0;
return 1;
}
int get_mincost(int s, int e)
{
int ans=0;
while(spfa(s, e))
{
int p = pre[e];
while(p!=-1)//更新边流量,最小费
{
Map[p].w -= flow[e];
Map[p^1].w += flow[e];
ans += flow[e] * Map[p].val;
p = pre[Map[p].u];
}
}
return ans;//返回最小费
}
int main()
{
int n, m, i, j;
while(~scanf("%d %d", &n, &m))
{
if(!n && !m) break;
cnt = 0;
memset(head, -1, sizeof(head));
vector<node> q, p;//q存人的坐标,p存房子的坐标
for(i = 0; i < n; i++)
{
scanf("%s", s[i]);
for(j = 0; j < m; j++)
{
if(s[i][j] == 'm')
q.push_back((node){i, j});
if(s[i][j] == 'H')
p.push_back((node){i, j});
}
}
int qmax = q.size(), pmax = p.size();
for(i = 0; i < q.size(); i++)
{
add(0, i+1, 1, 0);//超级源点到人
for(j = 0; j < p.size(); j++)
{
add(i+1, j+qmax+1, 1, value(q[i], p[j]));//人到房子
if(!i)
add(j+qmax+1, qmax+pmax+1, 1, 0);//房子到超级汇点
}
}
printf("%d\n", get_mincost(0, qmax+pmax+1));
}
}