【POJ 2195】 Going Home(KM算法求最小权匹配)
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 20303 | Accepted: 10297 |
Description
Input
Output
Sample Input
2 2 .m H. 5 5 HH..m ..... ..... ..... mm..H 7 8 ...H.... ...H.... ...H.... mmmHmmmm ...H.... ...H.... ...H.... 0 0
Sample Output
2 10 28
Source
越来越觉得KM算法是种很神奇的算法,或者说各种图论算法都好神奇……在或者说 图是一种神奇的东西。。。一个题可能有多种算法可以解决
这题之前一只用最小费做的,一直以为是唯一的做法。今天做KM突然发现 这题之前不是做过么……
这题用KM算法会快很多,而且代码量少一点。不过思想的算法,没法做什么比较。
题目大意就是一定数量的人和房子,保证人和房子数量相同,要求分配每个人到对应的房子里,并且人与房子一一对应。问所有人需要移动的最少步数。建图我是把人和房子的坐标用pair存成数组,然后求出每个人到每个房子的距离,也就是最初的二分图。
之后用KM求最小权,跟最大权一种套路,只不过建图的时候权值取负,这样求出的最大权其实绝对值是最小的。换句话说取反回来后,其实就是所求的解。
代码如下:
#include <iostream> #include <cmath> #include <vector> #include <cstdlib> #include <cstdio> #include <cstring> #include <queue> #include <list> #include <algorithm> #include <map> #include <set> #define LL long long #define fread() freopen("in.in","r",stdin) #define fwrite() freopen("out.out","w",stdout) #define Pr pair<int,int> using namespace std; const int INF = 0x3f3f3f3f; const int msz = 10000; const double eps = 1e-8; //二分图 int mp[233][233]; // x顶标 y顶标 int lx[233],ly[233],link[233],slick[233]; bool visx[233],visy[233]; // 人的坐标 房子坐标 Pr human[233],house[233]; //人数 房数 int nm,nh; bool cal(int x) { visx[x] = 1; for(int y = 0; y < nh; ++y) { if(visy[y]) continue; int t = lx[x]+ly[y]-mp[x][y]; if(t == 0) { visy[y] = 1; if(link[y] == -1 || cal(link[y])) { link[y] = x; return true; } } else slick[y] = min(slick[y],t); } return false; } int KM() { memset(link,-1,sizeof(link)); for(int i = 0; i < nm; ++i) { memset(slick,INF,sizeof(slick)); while(1) { memset(visx,0,sizeof(visx)); memset(visy,0,sizeof(visy)); if(cal(i)) break; int d = INF; for(int j = 0; j < nh; ++j) if(!visy[j]) d = min(d,slick[j]); for(int j = 0; j < nm; ++j) if(visx[j]) lx[j] -= d; for(int j = 0; j < nh; ++j) if(visy[j]) ly[j] += d; else slick[j] -= d; } } int ans = 0; for(int i = 0; i < nh; ++i) if(link[i] != -1) ans += mp[link[i]][i]; return ans; } int main() { int n,m; char tmp[233]; while(~scanf("%d%d",&n,&m) && (n+m)) { nm = nh = 0; for(int i = 0; i < n; ++i) { scanf("%s",tmp); for(int j = 0; j < m; ++j) if(tmp[j] == 'm') human[nm++] = Pr(i,j); else if(tmp[j] == 'H') house[nh++] = Pr(i,j); } memset(lx,0,sizeof(lx)); memset(ly,0,sizeof(ly)); for(int i = 0; i < nm; ++i) for(int j = 0; j < nh; ++j) { mp[i][j] = -(abs(human[i].first-house[j].first)+abs(human[i].second-house[j].second)); lx[i] = max(lx[i],mp[i][j]); } printf("%d\n",-KM()); } return 0; }