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....
*/

京公网安备 11010502036488号