Going Home

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)

Total Submission(s): 3443    Accepted Submission(s): 1763

Problem Description
On a grid map there are n little men and n houses. In each unit time, every little man can move one unit step, either horizontally, or vertically, to an adjacent point. For each little man, you need to pay a $1 travel fee for every step he moves, until he enters
a house. The task is complicated with the restriction that each house can accommodate only one little man. 



Your task is to compute the minimum amount of money you need to pay in order to send these n little men into those n different houses. The input is a map of the scenario, a '.' means an empty space, an 'H' represents a house on that point, and am 'm' indicates
there is a little man on that point. 



You can think of each point on the grid map as a quite large square, so it can hold n little men at the same time; also, it is okay if a little man steps on a grid with a house without entering that house.
 
Input
There are one or more test cases in the input. Each case starts with a line giving two integers N and M, where N is the number of rows of the map, and M is the number of columns. The rest of the input will be N lines describing the map. You may assume both
N and M are between 2 and 100, inclusive. There will be the same number of 'H's and 'm's on the map; and there will be at most 100 houses. Input will terminate with 0 0 for N and M.
 
Output
For each test case, output one line with the single integer, which is the minimum amount, in dollars, you need to pay. 
 
Sample Input
2 2
.m
H.
5 5
HH..m
.....
.....
.....
mm..H
7 8
...H....
...H....
...H....
mmmHmmmm
...H....
...H....
...H....
0 0
 
Sample Output
2
10
28
 

第一道费用流。 O(∩_∩)O~~

题意:一个N*M地图上有同样数量的字符H和字符m。m代表一个 人,H代表一个房子。人到房子的花销是它们在图中的曼哈顿距离,问你让全部人回到房子所须要的最小费用(一个房子仅仅能容纳一个人)。

思路:设置超级源点source 连接全部字符H,容量为1。费用为0。每一个字符H向全部字符m连边,容量为1,费用为它们的曼哈顿距离。最后每一个字符m向超级汇点sink连边。容量为1,费用为0。

最后source到sink跑一遍最小费用最大流就ok了。

(我建边时把字符H看做人了,所以建的反向边,对这道题没有影响)

AC代码:

#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define MAXN 200+10
#define MAXM 80000+100
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
int from, to, cap, flow, cost, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int pre[MAXN], dist[MAXN];
bool vis[MAXN];
int N, M;
int source, sink;//超级源点 超级汇点
void init()
{
edgenum = 0;
memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w, int c)
{
Edge E1 = {u, v, w, 0, c, head[u]};
edge[edgenum] = E1;
head[u] = edgenum++;
Edge E2 = {v, u, 0, 0, -c, head[v]};
edge[edgenum] = E2;
head[v] = edgenum++;
}
int dis(int x1, int y1, int x2, int y2)
{
return abs(x1 - x2) + abs(y1 - y2);
}
struct Node
{
int x, y;
};
Node m[110], H[110];//存储字符坐标
int m_cnt;//m字符计数器
int H_cnt;//H字符计数器
void getMap()
{
m_cnt = H_cnt = 0;
char str[110][110];
for(int i = 0; i < N; i++)
{
scanf("%s", str[i]);
for(int j = 0; j < M; j++)
{
if(str[i][j] == 'm')
{
++m_cnt;
m[m_cnt].x = i;
m[m_cnt].y = j;
}
if(str[i][j] == 'H')
{
++H_cnt;
H[H_cnt].x = i;
H[H_cnt].y = j;
}
}
}
int k = m_cnt;//人数 或者 房子数
source = 0;
sink = 2*k+1;
for(int i = 1; i <= k; i++)
{
addEdge(source, i, 1, 0);
addEdge(i + k, sink, 1, 0);
for(int j = 1; j <= k; j++)
{
int d = dis(H[i].x, H[i].y, m[j].x, m[j].y);
addEdge(i, j + k, 1, d);
}
}
}
bool SPFA(int s, int t)//寻找花销最少的路径
{
queue<int> Q;
memset(dist, INF, sizeof(dist));
memset(vis, false, sizeof(vis));
memset(pre, -1, sizeof(pre));
dist[s] = 0;
vis[s] = true;
Q.push(s);
while(!Q.empty())
{
int u = Q.front();
Q.pop();
vis[u] = false;
for(int i = head[u]; i != -1; i = edge[i].next)
{
Edge E = edge[i];
if(dist[E.to] > dist[u] + E.cost && E.cap > E.flow)//能够松弛 且 没有满流
{
dist[E.to] = dist[u] + E.cost;
pre[E.to] = i;//记录前驱边 的编号
if(!vis[E.to])
{
vis[E.to] = true;
Q.push(E.to);
}
}
}
}
return pre[t] != -1;//可达返回true
}
void MCMF(int s, int t, int &cost, int &flow)
{
flow = 0;//总流量
cost = 0;//总费用
while(SPFA(s, t))//每次寻找花销最小的路径
{
int Min = INF;
//通过反向弧 在源点到汇点的最少花费路径 找最小增广流
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
{
Edge E = edge[i];
Min = min(Min, E.cap - E.flow);
}
//增广
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
{
edge[i].flow += Min;
edge[i^1].flow -= Min;
cost += edge[i].cost * Min;//增广流的花销
}
flow += Min;//流量累加
}
}
int main()
{
while(scanf("%d%d", &N, &M), N||M)
{
init();
getMap();
int cost, flow;
MCMF(source, sink, cost, flow);
printf("%d\n", cost);
}
return 0;
}

KM算法: 重刷

求最大流。用负权,最后结果取反。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define INF 10000000
using namespace std;
int lx[110], ly[110];
int Map[110][110];
bool visx[110], visy[110];
int slack[110];
int match[110];
int nx, ny;
int N, M;
char str[110][110];
int dis(int x1, int y1, int x2, int y2)
{
return abs(x1 - x2) + abs(y1 - y2);
}
struct Node
{
int x, y;
};
Node m[110], H[110];//存储字符坐标
int m_cnt;//m字符计数器
int H_cnt;//H字符计数器
void getMap()
{
m_cnt = H_cnt = 0;
char str[110][110];
for(int i = 0; i < N; i++)
{
scanf("%s", str[i]);
for(int j = 0; j < M; j++)
{
if(str[i][j] == 'm')
{
++m_cnt;
m[m_cnt].x = i;
m[m_cnt].y = j;
}
if(str[i][j] == 'H')
{
++H_cnt;
H[H_cnt].x = i;
H[H_cnt].y = j;
}
}
}
int k = m_cnt;//人数 或者 房子数
nx = ny = k;
for(int i = 1; i <= k; i++)
{
for(int j = 1; j <= k; j++)
{
int d = dis(H[i].x, H[i].y, m[j].x, m[j].y);
Map[i][j] = -d;
}
}
}
int DFS(int x)
{
visx[x] = true;
for(int y = 1; y <= ny; y++)
{
if(visy[y]) continue;
int t = lx[x] + ly[y] - Map[x][y];
if(t == 0)
{
visy[y] = true;
if(match[y] == -1 || DFS(match[y]))
{
match[y] = x;
return 1;
}
}
else if(slack[y] > t)
slack[y] = t;
}
return 0;
}
void KM()
{
memset(match, -1, sizeof(match));
memset(ly, 0, sizeof(ly));
for(int x = 1; x <= nx; x++)
{
lx[x] = -INF;
for(int y = 1; y <= ny; y++)
lx[x] = max(lx[x], Map[x][y]);
}
for(int x = 1; x <= nx; x++)
{
for(int i = 1; i <= ny; i++)
slack[i] = INF;
while(1)
{
memset(visx, false, sizeof(visx));
memset(visy, false, sizeof(visy));
if(DFS(x)) break;
int d = INF;
for(int i = 1; i <= ny; i++)
{
if(!visy[i] && slack[i] < d)
d = slack[i];
}
for(int i = 1; i <= nx; i++)
{
if(visx[i])
lx[i] -= d;
}
for(int i = 1; i <= ny; i++)
{
if(visy[i])
ly[i] += d;
else
slack[i] -= d;
}
}
}
int ans = 0;
for(int i = 1; i <= ny; i++)
ans += Map[match[i]][i];
printf("%d\n", -ans);
}
int main()
{
while(scanf("%d%d", &N, &M), N||M)
{
getMap();
KM();
}
return 0;
}

hdoj 1533 Going Home 【最小费用最大流】【KM入门题】的更多相关文章

  1. hdu 1533 Going Home 最小费用最大流 (模板题)

    Going Home Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total ...

  2. hdu 1533 Going Home 最小费用最大流

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1533 On a grid map there are n little men and n house ...

  3. hdu 1533 Going Home 最小费用最大流 入门题

    Going Home Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Tota ...

  4. POJ 2195 & HDU 1533 Going Home(最小费用最大流)

    这就是一道最小费用最大流问题 最大流就体现到每一个'm'都能找到一个'H',但是要在这个基础上面加一个费用,按照题意费用就是(横坐标之差的绝对值加上纵坐标之差的绝对值) 然后最小费用最大流模板就是再用 ...

  5. poj2135最小费用最大流经典模板题

    Farm Tour Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 13509   Accepted: 5125 Descri ...

  6. hdoj 3488 Tour 【最小费用最大流】【KM算法】

    Tour Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submi ...

  7. Minimum Cost(最小费用最大流,好题)

    Minimum Cost http://poj.org/problem?id=2516 Time Limit: 4000MS   Memory Limit: 65536K Total Submissi ...

  8. POJ--2516--Minimum Cost【最小费用最大流】

    链接:http://poj.org/problem?id=2516 题意:有k种货物,n个客户对每种货物有一定需求量,有m个仓库.每一个仓库里有一定数量的k种货物.然后k个n*m的矩阵,告诉从各个仓库 ...

  9. POJ 3422 Kaka's Matrix Travels 【最小费用最大流】

    题意: 卡卡有一个矩阵,从左上角走到右下角,卡卡每次只能向右或者向下.矩阵里边都是不超过1000的正整数,卡卡走过的元素会变成0,问卡卡可以走k次,问卡卡最多能积累多少和. 思路: 最小费用最大流的题 ...

  10. POJ2135 最小费用最大流模板题

    练练最小费用最大流 此外此题也是一经典图论题 题意:找出两条从s到t的不同的路径,距离最短. 要注意:这里是无向边,要变成两条有向边 #include <cstdio> #include ...

随机推荐

  1. PHP中GD库函数

    画椭圆弧 imagearc($image,$cx,$cy,$width,$height,$angel1,$angel2,$color) 注释:$image 图像资源   $cx  椭圆中心点的水平位置 ...

  2. LeetCode(74) Search a 2D Matrix

    题目 Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the fo ...

  3. js正则替换十六进制

    var re=/\x62/;//没有0,也没有分号。alert(re.test("blue"));  //output "true" 需要使用< 如需显示 ...

  4. python025 Python3 正则表达式

    Python3 正则表达式 正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配. Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式. ...

  5. hexo干货系列:(四)将hexo博客同时托管到github和coding

    前言 之前我们把hexo托管在github,但是毕竟github是国外的,访问速度上还是有点慢,所以想也部署一套在国内的托管平台,之前查资料听说gitcafe,但是听说gitcafe已经被coding ...

  6. smartctl---查看硬件接口

    1.查看磁盘信息: #smartctl -i /dev/sda smartctl 5.42 2011-10-20 r3458 [x86_64-linux-2.6.18-308.16.1.el5] (l ...

  7. 【二分+尺取】HDU 6119 小小粉丝度度熊

    http://acm.hdu.edu.cn/showproblem.php?pid=6119 [思路] 首先通过处理交叉的可以处理成不交叉的 然后二分查找答案 如何判断一个长度是否可行? 双指针O(n ...

  8. P1979 [NOIP]华容道

    [问题描述] 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时间. 小 B 玩的华 ...

  9. 【收藏】下载Chrome商店插件的方法,万恶的gwd

    以下是下载离线插件包的方法: 第一步: 每个Google Chrome扩展都有一个固定的ID,例如https://chrome.google.com/webstore/detail/bfbmjmiod ...

  10. BZOJ1585: [Usaco2009 Mar]Earthquake Damage 2 地震伤害

    n<=3000个点m<=20000条无向边的图,有p<=n个出发点,每个出发点都不可拆,现拆一些点使每个出发点都不能到达点1,求最小点数. 简单的最小割.每个点拆成两个x和y,无向边 ...