1、poj 2195 Going Home(二分图最小权匹配)
题意:图上有n个房子,n个人,现在安排每个人回到一所房间,求最小的步数和。
思路:KM算法模板题。注意反向。
附:推荐km算法大神博客:
http://blog.sina.com.cn/s/blog_691ce2b701016reh.html
http://www.cnblogs.com/wenruo/p/5264235.html
http://blog.csdn.net/sixdaycoder/article/details/47720471
http://blog.csdn.net/zxn0803/article/details/49999267
http://www.cnblogs.com/Lanly/p/6291214.html
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 #define N 105 6 #define INF 0x3f3f3f 7 char maze[N][N]; 8 int mp[N][N], match[N], lx[N], ly[N], visx[N], visy[N], slack[N]; 9 int n, m,ny,nx; 10 //lx,ly为顶标,nx,ny分别为x点集y点集的个数 11 //match数组记录右边y端点所连的左端点x,visx,visy数组记录是否曾访问过,也是判断是否在增广路上 12 struct node 13 { 14 int a, b; 15 }sa[N], sb[N]; 16 //KM求二分图最小匹配模板:只需把权值都变成负的,再用KM算出最大权匹配,然后取反就是答案 17 //学习KM地址http://blog.sina.com.cn/s/blog_691ce2b701016reh.html 18 bool dfs(int x) 19 { 20 visx[x] = 1; 21 for (int y = 1; y <= ny; y++) 22 { 23 if (visy[y]) continue; 24 int t = lx[x] + ly[y] - mp[x][y]; 25 if (t == 0)//(x,y)在相等子图中 26 { 27 visy[y] = 1; 28 if (match[y] == -1 || dfs(match[y])) 29 {//注意这里要搜match[y]而不是y,因为我们只搜索x侧的,不需要搜索y侧的 30 match[y] = x; 31 return true; 32 } 33 } 34 else if (slack[y]>t) slack[y] = t;//(x,y)不在相等子图中且y不在交错树中slack 取最小的 35 } 36 return false; 37 } 38 39 int KM() 40 { 41 memset(match, -1, sizeof(match)); 42 memset(lx, -INF, sizeof(lx)); 43 memset(ly, 0, sizeof(ly)); 44 for (int i = 1; i <= nx; i++) 45 { 46 for (int j = 1; j <= ny; j++) 47 {//lx初始化为与它关联边中最大的 48 if (mp[i][j]>lx[i]) lx[i] = mp[i][j]; 49 } 50 } 51 for (int x = 1; x <= nx; x++) 52 { 53 for (int y = 1; y <= ny; y++) 54 slack[y] = INF; //每次换新的x结点都要初始化slack 55 while (1) 56 { 57 memset(visx, 0, sizeof(visx)); 58 memset(visy, 0, sizeof(visy));//这两个初始化必须放在这里,因此每次dfs()都要更新 59 if (dfs(x)) break; 60 //若成功(找到了增广轨),则该点增广完成,进入下一个点的增广 61 //若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的数量增加。方法为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全部减去一个常数d,所有在增广轨中的Y方点的标号全部加上一个常数d. 62 int d = INF; 63 for (int y = 1; y <= ny; y++) 64 { 65 if (!visy[y] && d>slack[y]) d = slack[y]; 66 } 67 for (int x = 1; x <= nx; x++) 68 { 69 if (visx[x]) lx[x] -= d; 70 } 71 for (int y = 1; y <= ny; y++) 72 {//修改顶标后,要把所有不在交错树中的Y顶点的slack值都减去d,这是因为lx[i] 减小了delta,slack[j] = min(lx[i] + ly[j] -w[i][j]) --j不属于交错树--也需要减少delta,第二类边 73 if (visy[y]) ly[y] += d; 74 else slack[y] -= d; 75 } 76 } 77 } 78 int res = 0; 79 for (int i = 1; i <= ny; i++) 80 { 81 if (match[i]>-1) res += mp[match[i]][i]; 82 } 83 return res; 84 } 85 86 int main() 87 { 88 int n, m; 89 while (~scanf("%d%d", &n, &m)) 90 { 91 if (n + m == 0) break; 92 for (int i = 1; i <= n; i++) 93 { 94 scanf("%s", maze[i] + 1); 95 } 96 int cnt1 = 0, cnt2 = 0; 97 for (int i = 1; i <= n; i++) 98 { 99 for (int j = 1; j <= m; j++) 100 { 101 if (maze[i][j] == 'm') 102 { 103 sa[++cnt1].a = i; 104 sa[cnt1].b = j; 105 } 106 if (maze[i][j] == 'H') 107 { 108 sb[++cnt2].a = i; 109 sb[cnt2].b = j; 110 } 111 } 112 } 113 int cnt = cnt1; 114 for (int i = 1; i <= cnt1; i++) 115 { 116 for (int j = 1; j <= cnt2; j++) 117 { 118 mp[i][j] = abs(sa[i].a - sb[j].a) + abs(sa[i].b - sb[j].b); 119 mp[i][j] = -mp[i][j];//取反求最大权匹配(也可以用一个极大值减去原来的值求最大权匹配) 120 } 121 } 122 ny = nx = cnt; 123 printf("%d\n", -KM());//再取反则为最小权 124 } 125 return 0; 126 }