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 }

 

posted @ 2014-04-02 22:22  辛力啤  阅读(195)  评论(0编辑  收藏  举报