【二分图匹配入门专题1】K
来源:互联网 发布:闪飞网络大师 编辑:程序博客网 时间:2024/05/22 15:33
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.
InputThere 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.
OutputFor 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.mH.5 5HH..m...............mm..H7 8...H.......H.......H....mmmHmmmm...H.......H.......H....0 0
Sample Out12
题意:输入一个n行m列的图,‘H’和'm'数目相等,从‘m’出发到'H',走一步花费加1,问,在满足每个'm'都能走到'H'的情况下,求出 总的最小花费。思路:求最小匹配,我们可以转换思路,用一个总的值sum-最大匹配,就是我们要求的最小匹配,比如说,我们要求一个集和中的 最小值min,那么我们用一个max减去集合中每一个数,再求出集合的最大值max2,我们所求的min = max-max2。 那么这道题中呢,只是把求最小值换为求最小和,我们同样可以用总和-最大和求得。(为什么让我想起了上次的最大子段和~~) 这个思路转换过来后,它就是一个km模板的套路题了。~~~早起鸟儿果然有虫吃,又get到一个新技能,自己这道题一开始求得最大匹配,后来发现无法求出最小匹配,后来看了别人的博客才知道 可以建图的时候转换一下,不过最大的收获就是我的模板又得到了优化,之前是slack数组去存每一个不在交错树中的tmp值,最后还要 进行比较得出最小常数d,现在呢,就完全不用了,我们直接定义一个全局d,每次dfs的时候都更新一遍,就不用像之前那样处理 不在交错树中的slack值
#include<stdio.h>#include<string.h>#define N 110#define INF 0x3f3f3f3fint n,m;char map[N][N];int sumhome,summan;int w[N][N],linker[N];int lx[N],ly[N],visx[N],visy[N];int ans,d,nx,ny;struct node{ int x,y;};node home[N],man[N];int abs(int a)//取绝对值 { if( a < 0) return -a; return a;}void Count()//编号 { int i,j; sumhome = summan = 0; for(i = 1; i <= n; i ++) for(j = 1; j <= m; j ++) { if(map[i][j] == 'H') { home[++sumhome].x = i; home[sumhome].y = j; } if(map[i][j] == 'm') { man[++summan].x = i; man[summan].y = j; } } return; }void getmap()//建图 { int i,j; nx = summan; ny = sumhome; for(i = 1; i <= nx; i ++) for(j = 1; j <= ny; j ++) { w[i][j] = abs(man[i].x - home[j].x)+abs(man[i].y - home[j].y); } return;}int dfs(int x)//完全匹配 { int y,tmp; visx[x] = 1; for(y = 1; y <= ny; y ++) { if(!visy[y]) { tmp = lx[x] + ly[y] - w[x][y]; if(!tmp) { visy[y] = 1; if(linker[y] == -1||dfs(linker[y])) { linker[y] = x; return 1; } } else if(d > tmp)//取最小的不在增广轨中的常数d d = tmp; } } return 0;}int KM()//求最小匹配 { int sum,x,i,j; memset(linker,-1,sizeof(linker)); memset(ly,0,sizeof(ly)); for(i = 1; i <= nx; i ++) for(j = 1; j <= ny; j ++) w[i][j] = 100*100 - w[i][j];//极大值减去原来的值 for(i = 1; i <= nx; i ++) for(j = 1,lx[i] = -INF; j <= ny; j ++) if(lx[i] < w[i][j]) lx[i] = w[i][j];//初始化为权值最大的边的权值 for(x = 1; x <= nx; x++) { while(1) { d = INF;//常数d每次都要进行初始化 memset(visx,0,sizeof(visx));//每次dfs都要进行更新 memset(visy,0,sizeof(visy)); if(dfs(x)) break; for(i = 1; i <= nx; i ++) if(visx[i])//在增广轨中的x点标减去常数d lx[i] -= d; for(i = 1; i <= ny; i ++) if(visy[i])//在增广轨中的y点标加上常数d ly[i] += d; } } sum = 0; for(i = 1; i <= ny; i ++) if(linker[i]!=-1) sum += w[linker[i]][i]; sum = nx*100*100-sum;//总值-最大匹配,就是我们所求的最小匹配 return sum;}int main(){ int i,j; while(scanf("%d%d",&n,&m),(m+n)!=0) { getchar(); for(i = 1; i <= n; i ++) scanf("%s",map[i]+1); Count();//对home,man进行编号 getmap();//得到带权值的图 ans = KM();//km算法 printf("%d\n",ans);//输出最小匹配 } return 0;}
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.
InputThere 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.
OutputFor 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.mH.5 5HH..m...............mm..H7 8...H.......H.......H....mmmHmmmm...H.......H.......H....0 0
Sample Out12
题意:输入一个n行m列的图,‘H’和'm'数目相等,从‘m’出发到'H',走一步花费加1,问,在满足每个'm'都能走到'H'的情况下,求出 总的最小花费。思路:求最小匹配,我们可以转换思路,用一个总的值sum-最大匹配,就是我们要求的最小匹配,比如说,我们要求一个集和中的 最小值min,那么我们用一个max减去集合中每一个数,再求出集合的最大值max2,我们所求的min = max-max2。 那么这道题中呢,只是把求最小值换为求最小和,我们同样可以用总和-最大和求得。(为什么让我想起了上次的最大子段和~~) 这个思路转换过来后,它就是一个km模板的套路题了。~~~早起鸟儿果然有虫吃,又get到一个新技能,自己这道题一开始求得最大匹配,后来发现无法求出最小匹配,后来看了别人的博客才知道 可以建图的时候转换一下,不过最大的收获就是我的模板又得到了优化,之前是slack数组去存每一个不在交错树中的tmp值,最后还要 进行比较得出最小常数d,现在呢,就完全不用了,我们直接定义一个全局d,每次dfs的时候都更新一遍,就不用像之前那样处理 不在交错树中的slack值
#include<stdio.h>#include<string.h>#define N 110#define INF 0x3f3f3f3fint n,m;char map[N][N];int sumhome,summan;int w[N][N],linker[N];int lx[N],ly[N],visx[N],visy[N];int ans,d,nx,ny;struct node{ int x,y;};node home[N],man[N];int abs(int a)//取绝对值 { if( a < 0) return -a; return a;}void Count()//编号 { int i,j; sumhome = summan = 0; for(i = 1; i <= n; i ++) for(j = 1; j <= m; j ++) { if(map[i][j] == 'H') { home[++sumhome].x = i; home[sumhome].y = j; } if(map[i][j] == 'm') { man[++summan].x = i; man[summan].y = j; } } return; }void getmap()//建图 { int i,j; nx = summan; ny = sumhome; for(i = 1; i <= nx; i ++) for(j = 1; j <= ny; j ++) { w[i][j] = abs(man[i].x - home[j].x)+abs(man[i].y - home[j].y); } return;}int dfs(int x)//完全匹配 { int y,tmp; visx[x] = 1; for(y = 1; y <= ny; y ++) { if(!visy[y]) { tmp = lx[x] + ly[y] - w[x][y]; if(!tmp) { visy[y] = 1; if(linker[y] == -1||dfs(linker[y])) { linker[y] = x; return 1; } } else if(d > tmp)//取最小的不在增广轨中的常数d d = tmp; } } return 0;}int KM()//求最小匹配 { int sum,x,i,j; memset(linker,-1,sizeof(linker)); memset(ly,0,sizeof(ly)); for(i = 1; i <= nx; i ++) for(j = 1; j <= ny; j ++) w[i][j] = 100*100 - w[i][j];//极大值减去原来的值 for(i = 1; i <= nx; i ++) for(j = 1,lx[i] = -INF; j <= ny; j ++) if(lx[i] < w[i][j]) lx[i] = w[i][j];//初始化为权值最大的边的权值 for(x = 1; x <= nx; x++) { while(1) { d = INF;//常数d每次都要进行初始化 memset(visx,0,sizeof(visx));//每次dfs都要进行更新 memset(visy,0,sizeof(visy)); if(dfs(x)) break; for(i = 1; i <= nx; i ++) if(visx[i])//在增广轨中的x点标减去常数d lx[i] -= d; for(i = 1; i <= ny; i ++) if(visy[i])//在增广轨中的y点标加上常数d ly[i] += d; } } sum = 0; for(i = 1; i <= ny; i ++) if(linker[i]!=-1) sum += w[linker[i]][i]; sum = nx*100*100-sum;//总值-最大匹配,就是我们所求的最小匹配 return sum;}int main(){ int i,j; while(scanf("%d%d",&n,&m),(m+n)!=0) { getchar(); for(i = 1; i <= n; i ++) scanf("%s",map[i]+1); Count();//对home,man进行编号 getmap();//得到带权值的图 ans = KM();//km算法 printf("%d\n",ans);//输出最小匹配 } return 0;}
- 【二分图匹配入门专题1】K
- 【二分图匹配入门专题1】A
- 【二分图匹配入门专题1】B
- 【二分图匹配入门专题1】C
- 【二分图匹配入门专题1】D
- 【二分图匹配入门专题1】F
- 【二分图匹配入门专题1】E
- 【二分图匹配入门专题1】H
- 【二分图匹配入门专题1】I
- 【二分图匹配入门专题1】L
- 【二分图匹配入门专题1】M
- 【二分图匹配入门专题1】 N
- 【二分图匹配入门专题1】O
- 【二分匹配入门专题1】G
- 【二分匹配入门专题1】J
- 【二分匹配入门专题1】P
- 专题:二分图匹配
- 【二分图匹配入门专题1】poj3686 【km+思维建图】
- 15-SpringBoot——Spring MVC基础-服务端推送技术
- 20170813(两道题-dijkstra记录路径 BFS)
- php:布尔值(boolean)数据类型判断
- USACO 2012 Mar Bronze 1.Times 17
- OGNL基础语法和与struts2的结合使用。
- 【二分图匹配入门专题1】K
- PullToRefreshListView
- Eclipse快捷键大全
- C++ memset详解
- [小结] 数论题的式子的转化
- Hibernate详解(一)--session操作对象
- 充值支付小程序案例-小程序支付开发-视频教程6
- java发送邮件 示例代码
- C++ Primer Notes(8)