hdoj 1533 Going Home 【最小费用最大流】【KM入门题】
Going HomeTime Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
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
Sample Output
|
第一道费用流。 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入门题】的更多相关文章
- hdu 1533 Going Home 最小费用最大流 (模板题)
Going Home Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total ...
- 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 ...
- hdu 1533 Going Home 最小费用最大流 入门题
Going Home Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Tota ...
- POJ 2195 & HDU 1533 Going Home(最小费用最大流)
这就是一道最小费用最大流问题 最大流就体现到每一个'm'都能找到一个'H',但是要在这个基础上面加一个费用,按照题意费用就是(横坐标之差的绝对值加上纵坐标之差的绝对值) 然后最小费用最大流模板就是再用 ...
- poj2135最小费用最大流经典模板题
Farm Tour Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 13509 Accepted: 5125 Descri ...
- hdoj 3488 Tour 【最小费用最大流】【KM算法】
Tour Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others) Total Submi ...
- Minimum Cost(最小费用最大流,好题)
Minimum Cost http://poj.org/problem?id=2516 Time Limit: 4000MS Memory Limit: 65536K Total Submissi ...
- POJ--2516--Minimum Cost【最小费用最大流】
链接:http://poj.org/problem?id=2516 题意:有k种货物,n个客户对每种货物有一定需求量,有m个仓库.每一个仓库里有一定数量的k种货物.然后k个n*m的矩阵,告诉从各个仓库 ...
- POJ 3422 Kaka's Matrix Travels 【最小费用最大流】
题意: 卡卡有一个矩阵,从左上角走到右下角,卡卡每次只能向右或者向下.矩阵里边都是不超过1000的正整数,卡卡走过的元素会变成0,问卡卡可以走k次,问卡卡最多能积累多少和. 思路: 最小费用最大流的题 ...
- POJ2135 最小费用最大流模板题
练练最小费用最大流 此外此题也是一经典图论题 题意:找出两条从s到t的不同的路径,距离最短. 要注意:这里是无向边,要变成两条有向边 #include <cstdio> #include ...
随机推荐
- 题目:企业发放的奖金根据利润提成。 利润(I)低于或等于10万元时,奖金可提10%; 利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可可提成7.5%; 20万到40万之间时,高于20万元的部分,可提成5%; 40万到60万之间时高于40万元的部分,可提成 3%; 60万到100万之间时,高于60万元的部分,可提成1.5%; 高于100万元时,超过
题目:企业发放的奖金根据利润提成. 利润(I)低于或等于10万元时,奖金可提10%: 利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可可提成7.5%: 20万到 ...
- C++类的存储及虚函数实现原理
一.C++成员函数在内存中的存储方式 用类去定义对象时,系统会为每一个对象分配存储空间.如果一个类包括了数据和函数,要分别为数据和函数的代码分配存储空间.按理说,如果用同一个类定义了10个对象,那么就 ...
- HTML基础(四)表格
定义和用法 <table> 标签定义 HTML 表格. 简单的 HTML 表格由 table 元素以及一个或多个 tr.th 或 td 元素组成. tr 元素定义表格行,th 元素定义表头 ...
- 【收藏】史上最全的浏览器 CSS & JS Hack 手册
浏览器渲染页面的方式各不相同,甚至同一浏览器的不同版本(“杰出代表”是 IE)也有差异.因此,浏览器兼容成为前端开发人员的必备技能.如果有一份浏览器 Hack 手册,那查询起来就方便多了.这篇文章就向 ...
- vue 中scroll事件不触发问题
在vue项目中需要监听滚动条滚动的位置,结果写了scroll监听事件就是不生效,最后查资料发现是页面有样式设置了over-flow:scroll,去掉之后完美解决.(页面样式中存在over-flow: ...
- 9. FILES
9. FILES FILES表提供有关存储MySQL表空间数据的文件的信息. FILES表提供有关InnoDB数据文件的信息. 在NDB Cluster中,此表还提供有关存储NDB Cluster D ...
- Cable master 求电缆的最大长度(二分法)
Description Inhabitants of the Wonderland have decided to hold a regional programming contest. The J ...
- javax.servlet.jsp.JspTagException: Neither BindingResult nor plain target object for bean (蛋疼死我了)
1为抛出异常原因,2为异常解决方法. 原因: 进入spring:bind标签源码你可以看到 Object target = requestContext.getModelObject(beanNa ...
- 洛谷 P3387 【模板】缩点 DAGdp学习记
我们以洛谷P3387 [模板]缩点 来学习DAGdp 1.这道题的流程 //伪代码 for i->n if(i未被遍历) tarjan(i) 缩点() DAGdp() 完成 首先tarjan这部 ...
- POJ-1061青蛙的约会,扩展欧几里德求逆元!
青蛙的约会 以前不止一次看过这个题,但都没有去补..好吧,现在慢慢来做. 友情提示 ...