poj 2195 二分匹配KM算法
1 /* 2 题意:给出一个n*m的图,H表示房子,m表示人的初始位置,.表示空位置,每个人从他的位置(x1,y1)走到H的位置(x2,y2)需要花费 3 sum = abs(x1-x2) + abs(y1-y2),每个人都必须走进一间房子,且每间房子都只能容纳一个人,问最少花费多少使得所有的人都走 4 进房间里 5 6 题解:权最大完美匹配(KM算法) 7 以n个人和n个房间建图,边权为人i走到房间j所需要的花费,然后求最大完美匹配即可。 8 */ 9 #include <cstdio> 10 #include <cstring> 11 12 #define MAXV 105 13 14 const int INF = (1<<31)-1; 15 16 struct node 17 { 18 int x,y; 19 }man[MAXV],home[MAXV]; // 记录所有人和房间的位置 20 int mnum,hnum; // 人和房间的数量 21 int n,m; 22 23 int w[MAXV][MAXV]; // 记录人走到所有房间的权值 24 25 int abs(int t) 26 { 27 return t > 0 ? t : -t; 28 } 29 30 int lx[MAXV],ly[MAXV],match[MAXV]; 31 int slack[MAXV]; 32 bool visx[MAXV],visy[MAXV]; 33 34 bool find(int u) 35 { 36 visx[u] = true; 37 for(int i=0; i<m; i++) 38 { 39 if (visy[i]) 40 continue; 41 if (lx[u] + ly[i] == w[u][i]) 42 { 43 visy[i] = true; 44 if (match[i] == -1 || find(match[i])) 45 { 46 match[i] = u; 47 return true; 48 } 49 } 50 else if (slack[i] > lx[u] + ly[i] - w[u][i]) 51 slack[i] = lx[u] + ly[i] - w[u][i]; 52 } 53 return false; 54 } 55 56 int KM() 57 { 58 memset(ly,0,sizeof(ly)); 59 for(int i=0; i<n; i++) 60 { 61 lx[i] = -INF; 62 for(int j=0; j<m; j++) 63 if (lx[i] < w[i][j]) 64 lx[i] = w[i][j]; 65 } 66 67 memset(match, -1, sizeof(match)); 68 for(int i=0; i<n ;i++) 69 { 70 for(int j=0; j<m; j++) 71 slack[j] = INF; 72 while (true) 73 { 74 memset(visx,false,sizeof(visx)); 75 memset(visy,false,sizeof(visy)); 76 if (find(i)) 77 break; 78 79 int d = INF; 80 for(int j=0; j<m; j++) 81 if (!visy[j] && d > slack[j]) 82 d = slack[j]; 83 for(int j=0; j<n; j++) 84 if (visx[j]) 85 lx[j] -= d; 86 for(int j=0; j<m; j++) 87 { 88 if (visy[j]) 89 ly[j] += d; 90 else 91 slack[j] -= d; 92 } 93 } 94 } 95 96 int sum = 0; 97 for(int i=0; i<n; i++) 98 if (match[i] != -1) 99 sum += w[match[i]][i]; 100 return sum; 101 } 102 103 int main(void) 104 { 105 while (~scanf("%d%d",&n,&m), n || m) 106 { 107 mnum = hnum = 0; 108 char s[105]; 109 for(int i=0; i<n; i++) 110 { 111 scanf("%s",s); 112 for(int j=0; j<m; j++) 113 { 114 if ('H' == s[j]) 115 { 116 home[hnum].x = i; 117 home[hnum].y = j; 118 hnum ++; 119 } 120 else if ('m' == s[j]) 121 { 122 man[mnum].x = i; 123 man[mnum].y = j; 124 mnum ++; 125 } 126 } 127 } 128 129 for(int i=0; i<mnum; i++) 130 for(int j=0; j<hnum; j++) 131 { 132 w[i][j] = -abs(man[i].x - home[j].x) - abs(man[i].y - home[j].y); // 初始为负值,这样最后取反得到最小值 133 } 134 n = mnum; 135 m = hnum; 136 printf("%d\n",-KM()); 137 } 138 return 0; 139 }