题解 | #Going Home#
Going Home
题目描述:
给你一张n * m的地图,里面有若干个小人,用'm'表示,同样的,有相同数量的房子,用'H'表示,每个人可以进入任意一个房子,不过每个房子只能住一个人,现在你需要计算所有人住满房子最少需要走多少步
思路:
因为房子数量=人的数量,所以是在二分图的完美匹配的基础上求最小权值和
之前求的都是最大权值和,这里求最小,只需要在建图的时候处理成负权边即可
我们只需要按照左集合是人,右集合是房子,来连边即可,连边之前需要预处理出来哪些点需要连,权值是多少
我这里用两个内嵌结构体点vector来分别存人和房子
每个人对每个房子去求权值,也就是两个点横坐标与纵坐标的和的差的绝对值的相反数(即
预处理完了以后就套KM的板子即可
#include<map> #include<set> #include<stack> #include<queue> #include<cmath> #include<cstdio> #include<string> #include<vector> #include<sstream> #include<cstring> #include<stdlib.h> #include<iostream> #include<algorithm> using namespace std; #define endl '\n' #define inf 0x3f3f3f3f #define MAX 100 + 50 #define mod 1000000007 #define lowbit(x) (x & (-x)) #define sd(n) scanf("%d",&n) #define sdd(n,m) scanf("%d %d",&n,&m) #define pd(n) printf("%d\n", (n)) #define pdd(n,m) printf("%d %d\n",n, m) #define sddd(n,m,z) scanf("%d %d %d",&n,&m,&z) #define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0) #define mem(a,b) memset((a),(b),sizeof(a)) typedef long long ll ; typedef unsigned long long ull; //不开longlong见祖宗!提交不看数据范围见祖宗! inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;} int n, m, num; char x; int tr[MAX][MAX];//最终的图的权值 struct ran{//记录点的横纵坐标 int x, y; }; //KM板子 int lx[MAX], ly[MAX], link[MAX]; bool visx[MAX], visy[MAX]; bool dfs(int x){ visx[x] = 1; for(int i = 1; i <= num; ++i){ if(!visy[i] && lx[x] + ly[i] == tr[x][i]){ visy[i] = 1; if(link[i] == -1 || dfs(link[i])){ link[i] = x; return true; } } } return false; } int KM(){ mem(ly, 0); mem(lx, 0xf7); mem(link, -1); for(int i = 1; i <= num; ++i){ for(int j = 1; j <= num; ++j){ lx[i] = max(lx[i], tr[i][j]); } } for(int i = 1; i <= num; ++i){ while (1) { mem(visy, 0); mem(visx, 0); if(dfs(i))break; int d = inf; for(int j = 1; j <= num; ++j){ if(visx[j]){ for(int k = 1; k <= num; ++k){ if(!visy[k]){ d = min(d, lx[j] + ly[k] - tr[j][k]); } } } } if(d == inf)return -1; for(int j = 1; j <= num; ++j){ if(visx[j])lx[j] -= d; } for(int j = 1; j <= num; ++j){ if(visy[j])ly[j] += d; } } } int ans = 0; for(int i = 1; i <= num; ++i){ ans += tr[link[i]][i]; } ans = 0 - ans; return ans; } int main(){ while (sdd(n, m) && n + m) {//多组输入 num = 0;//记录人的数量 vector<ran>vh;//房子的坐标 vector<ran>vm;//人的坐标 for(int i = 1; i <= n; ++i){ for(int j = 1; j <= m; ++j){ cin>>x; if(x == 'H'){ ++num; vh.push_back({i,j}); } else if(x == 'm'){ vm.push_back({i, j}); } } } for(int i = 0; i < vh.size(); ++i){//建图 for(int j = 0; j < vm.size(); ++j){ tr[i + 1][j + 1] = 0 - (abs(vh[i].x - vm[j].x) + abs(vh[i].y - vm[j].y)); } } cout<<KM()<<endl; } return 0; } /* 7 8 ...H.... ...H.... ...H.... mmmHmmmm ...H.... ...H.... ...H.... */