
2 2 .m H. 5 5 HH..m ..... ..... ..... mm..H 7 8 ...H.... ...H.... ...H.... mmmHmmmm ...H.... ...H.... ...H.... 0 0
2 10 28
一个N*M地图上有相同数量的字符H和字符m,m代表一个 人,H代表一个房子。人到房子的花销是它们在图中的曼哈顿距离,问你让所有人回到房子所需要的最小费用(一个房子只能容纳一个人)。
解题:
第一道最小费用流的题,没敢写难题,选了个裸题来A,感谢斌神的模板, 感谢小B的解析。
因为是裸题,思路还是比较好想的,简单说一下建图思路:
(1)建立超级源点outset, 超级汇点inset。
(2)源点向每个人建边, 容量为1, 费用为0。
(3)房子向汇点建边,容量为1, 费用为0。
(4)每个人和房子建边,容量为1,费用为曼哈顿距离。
从outset到inset跑一遍最小费用最大流就行了
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define INF 0x3f3f3f3f
#define maxn 220
#define maxm 88000
using namespace std;
int n, m;
int outset;//超级源点
int inset;//超级汇点
struct node {
int u, v, cap, flow, cost, next;
};
node edge[maxm];
int head[maxn], cnt;
int per[maxn];//记录增广路径上 到达点i的边的编号
int dist[maxn], vis[maxn];
void init(){
cnt = 0;
memset(head, -1, sizeof(head));
}
void add(int u, int v, int w, int c){
edge[cnt] = {u, v, w, 0, c, head[u]};
head[u] = cnt++;
edge[cnt] = {v, u, 0, 0, -c, head[v]};
head[v] = cnt++;
}
int dir(int x1, int y1, int x2, int y2){
return abs(x1 - x2) + abs(y1 - y2);
}
struct NODE{
int x, y;
};
NODE maph[maxn], mapm[maxn];
int ans_h, ans_m;
void getmap(){
char str[120][120];
ans_h = ans_m = 0;
for(int i = 0; i < n; ++i){
scanf("%s", str[i]);
for(int j = 0; j < m; ++j){
if(str[i][j] == 'm'){
ans_m++;
mapm[ans_m].x = i;
mapm[ans_m].y = j;
}
if(str[i][j] == 'H'){
ans_h++;
maph[ans_h].x = i;
maph[ans_h].y = j;
}
}
}
int t = ans_h;//人数
outset = 0;//源点
inset = t * 2 + 1;//汇点
for(int i = 1; i <= t; ++i){
add(outset, i, 1, 0);
add(i + t, inset, 1, 0);
for(int j = 1; j <= t; ++j){
int d = dir(maph[i].x, maph[i].y, mapm[j].x, mapm[j].y);
add(i, j + t, 1, d);
}
}
}
//寻找花销最小的路径
//跑一遍SPFA 找st——ed的最少花销路径 且该路径上每一条边不能满流
//若存在 说明可以继续增广,反之不能
bool SPFA(int st, int ed){
queue<int>q;
memset(dist, INF, sizeof(dist));
memset(vis, 0, sizeof(vis));
memset(per, -1, sizeof(per));
dist[st] = 0;
vis[st] = 1;
q.push(st);
while(!q.empty()){
int u = q.front();
q.pop();
vis[u] = 0;
for(int i = head[u]; i != -1; i = edge[i].next){
node E = edge[i];
if(dist[E.v] > dist[u] + E.cost && E.cap > E.flow){//可以松弛 且 没有满流
dist[E.v] = dist[u] + E.cost;
per[E.v] = i;//记录到达这个点的边的编号
if(!vis[E.v]){
vis[E.v] = 1;
q.push(E.v);
}
}
}
}
return per[ed] != -1;
}
void MCMF(int st, int ed, int &cost, int &flow){
flow = 0;//总流量
cost = 0;//总费用
while(SPFA(st, ed)){//每次寻找花销最小的路径
int mins = INF;
//通过反向弧 在源点到汇点的最少花费路径 找最小增广流
for(int i = per[ed]; i != -1; i = per[edge[i ^ 1].v]){
mins = min(mins, edge[i].cap - edge[i].flow);
}
//增广
for(int i = per[ed]; i != -1; i = per[edge[i ^ 1].v]){
edge[i].flow += mins;
edge[i ^ 1].flow -= mins;
cost += edge[i].cost * mins;//增广流的花销
}
flow += mins//总流量累加
}
}
int main (){
while(scanf("%d%d", &n, &m), n || m){
init();
getmap();
int cost, flow;//最小费用 最大流
MCMF(outset,inset, cost, flow);
printf("%d\n", cost);
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
HDU 1533--Going Home【最小费用最大流 && 模板】
原文地址:http://blog.csdn.net/hpuhjh/article/details/48035993