题目描述:
n 个小人回到 n 间房子,要求一对一,告诉每个人的位置和每个房子的位置,问n个人移动的总距离最少是多少
题解:
最小权值匹配模板
我们分别记录人和房,然后人与房连边并记录边权
将所有边权值取相反数,然后跑一遍最优权值匹配模板
详细看代码,仔细看看怎么构造图
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int inf = 0x3f3f3f3f; const int maxn = 300 + 10; int sx[maxn], sy[maxn], vx[maxn], vy[maxn], match[maxn]; int money[maxn][maxn], slack[maxn]; int n; int x,y; bool dfs(int x) { vx[x] = true; for(int i = 1; i <= n; i++) { int xx= sx[x] + sy[i] - money[x][i]; if(!vy[i] && !xx) { vy[i] = true; if(match[i]==-1 || dfs(match[i])) { match[i] = x; return true; } } else if(slack[i] > xx) slack[i] = xx; } return false; } void bestmatch() { for(int i = 1; i <= n; i++) { while(1) { memset(vx, false, sizeof(vx)); memset(vy, false, sizeof(vy)); memset(slack, 0x3f, sizeof(slack)); if(dfs(i)) break; int dd = inf; for(int i = 1; i <= n; i++) if(!vy[i] && dd>slack[i]) dd = slack[i]; for(int i = 1; i <= n; i++) { if(vx[i]) sx[i] -= dd; if(vy[i]) sy[i] += dd; } } } } struct node{ int x,y; }H[maxn],m[maxn]; int main() { while(~scanf("%d%d", &x,&y)) { if(x==0&&y==0)break; int tot1=0; int tot2=0; for(int i=1;i<=x;i++) { for(int j=1;j<=y;j++) { char x; cin>>x; if(x=='H') { H[++tot1].x=i; H[tot1].y=j; } else if(x=='m') { m[++tot2].x=i; m[tot2].y=j; } else continue; } } memset(sy, 0, sizeof(sy)); for(int i=1;i<=tot1;i++) { for(int j=1;j<=tot2;j++) { money[i][j]=(abs(H[i].x-m[j].x)+abs(H[i].y-m[j].y))*(-1); sx[i] = max(sx[i], money[i][j]); } } n=tot1; memset(match, -1, sizeof(match)); bestmatch(); int sum = 0; for(int i = 1; i <= n; i++) sum += money[match[i]][i]; printf("%d\n", sum*(-1)); } return 0; }