题目大意是一张地图中,有n个人要走回n个房子里,然后人只能横着或竖着走一格,求他们回家的距离总和最短。
这道题看起来是个最优匹配问题,用KM或者最小费用最大流做
首先把每个人与每个房子之间的距离求出来
然后就是套用模板了
#include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> using namespace std; struct point { int x, y; }p[105], h[105]; int m, n; int d[105][105]; char s[105][105]; int tot = 0; class mincost { private: const static int V = 205; //注意点的个数, 应该为人+房间+2, 所以至少要开到202 const static int E = 25015; const static int INF = -1u >> 1; struct Edge { int v, cap, cost; Edge *next; } pool[E], *g[V], *pp, *pree[V]; int T, S, dis[V], pre[V]; int n, m, flow, cirq[V]; void SPFA(); inline void addedge(int i, int j, int cap, int cost); public: bool initialize(int x, int y); void mincost_maxflow(); }; void mincost::mincost_maxflow() { while (true) { SPFA(); if (dis[T] == INF) break; int minn = INF; for (int i = T; i != S; i = pre[i]) minn = min(minn, pree[i]->cap); for (int i = T; i != S; i = pre[i]) { pree[i]->cap -= minn; pool[(pree[i] - pool)^0x1].cap += minn; flow += minn * pree[i]->cost; } tot += minn; //流量计算 } printf("%d\n", flow); } void mincost::SPFA() { bool vst[V] = {false}; int tail = 0, u; fill(dis,dis + n,0x7fffffff); cirq[0] = S; vst[S] = 1; dis[S] = 0; for (int i = 0; i <= tail; i++) { int v = cirq[i % n]; for (Edge *i = g[v]; i != NULL; i = i->next) { if (!i->cap) continue; u = i->v; if (i->cost + dis[v] < dis[u]) { dis[u] = i->cost + dis[v]; pree[u] = i; pre[u] = v; if (!vst[u]) { tail++; cirq[tail % n] = u; vst[u] = true; } } } vst[v] = false; } } void mincost::addedge(int i, int j, int cap, int cost) { pp->cap = cap; pp->v = j; pp->cost = cost; pp->next = g[i]; g[i] = pp++; } bool mincost::initialize(int x, int y) { memset(g, 0, sizeof (g)); pp = pool; n = x + y + 2; //n即为顶点的个数 S = 0; T = x + y + 1; for(int i = 1; i <= x; i++) { for(int j = 1; j <= y; j++) { addedge(i, x + j, 1, d[i][j]); addedge(x + j, i, 0, -d[i][j]); } } for(int i = 1; i <= x; i++) { addedge(0, i, 1, 0); addedge(i, 0, 0, 0); } for(int i = 1; i <= y; i++) { addedge(x + i, T, 1, 0); addedge(T, x + i, 0, 0); } flow = 0; return true; } mincost g; int main() { while(scanf("%d%d", &m, &n) != EOF) { if(m == 0 && n == 0) break; for(int i = 0; i < m; i++) scanf("%s", s[i]); int hcnt = 0, pcnt = 0; for(int i = 0; i < m; i++) { for(int j = 0; j < n; j++) { if(s[i][j] == 'H') { hcnt++; h[hcnt].x = i; h[hcnt].y = j; } else if(s[i][j] == 'm') { pcnt++; p[pcnt].x = i; p[pcnt].y = j; } } } for(int i = 1; i <= pcnt; i++) { for(int j = 1; j <= hcnt; j++) { d[i][j] = abs(p[i].x - h[j].x) + abs(p[i].y - h[j].y); } } g.initialize(pcnt, hcnt); tot = 0; g.mincost_maxflow(); } return 0; }
KM算法版本, 用的DD神牛的模板http://cuitianyi.com/blog/%E6%B1%82%E6%9C%80%E5%A4%A7%E6%9D%83%E4%BA%8C%E5%88%86%E5%8C%B9%E9%85%8D%E7%9A%84km%E7%AE%97%E6%B3%95/
另外 芳哥blog里有篇介绍http://blog.csdn.net/wsniyufang/article/details/6759628
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <queue> #define MAXN 105 #define MAXM 555555 #define INF 1000000000 using namespace std; char mp[MAXN][MAXN]; int n, m, ny, nx; int w[MAXN][MAXN]; int lx[MAXN], ly[MAXN]; int linky[MAXN]; int visx[MAXN], visy[MAXN]; int slack[MAXN]; struct P { int x, y; }house[MAXN], man[MAXN]; bool find(int x) { visx[x] = 1; for(int y = 1; y <= ny; y++) { if(visy[y]) continue; int t = lx[x] + ly[y] - w[x][y]; if(t == 0) { visy[y] = 1; if(linky[y] == -1 || find(linky[y])) { linky[y] = x; return true; } } else if(slack[y] > t) slack[y] = t; } return false; } void KM() { memset(linky, -1, sizeof(linky)); for(int i = 1; i <= nx; i++) lx[i] = -INF; memset(ly, 0, sizeof(ly)); for(int i = 1; i <= nx; i++) for(int j = 1; j <= ny; j++) if(w[i][j] > lx[i]) lx[i] = w[i][j]; for(int x = 1; x <= nx; x++) { for(int i = 1; i <= ny; i++) slack[i] = INF; while(true) { memset(visx, 0, sizeof(visx)); memset(visy, 0, sizeof(visy)); if(find(x)) break; int d = INF; for(int i = 1; i <= ny; i++) if(!visy[i]) d = min(d, slack[i]); for(int i = 1; i <= nx; i++) if(visx[i]) lx[i] -=d; for(int i = 1; i <= ny; i++) if(visy[i]) ly[i] += d; else slack[i] -= d; } } } int main() { while(scanf("%d%d", &n, &m) != EOF) { if(n == 0 && m == 0) break; ny = 0, nx = 0; for(int i = 0; i < n; i++) scanf("%s", mp[i]); for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) if(mp[i][j] == 'H') {++ny; house[ny].x = i, house[ny].y = j;} else if(mp[i][j] == 'm') {++nx; man[nx].x = i, man[nx].y = j;} for(int i = 1; i <= nx; i++) for(int j = 1; j <= ny; j++) w[i][j] = -(abs(house[j].x - man[i].x) + abs(house[j].y - man[i].y)); KM(); int ans = 0; for(int i = 1; i <= ny; i++) ans += w[linky[i]][i]; printf("%d\n", -ans); } return 0; }