传送门:Going Home
题意为,在一块 n*m 的棋盘上,散落着数量相等的人与房子,每个人都可以得到且只能得到一间房子,所花费的代价是其移动到该房子的花费(人可以花费 1 旅费向四个方向种的一个移动一个单位),求所有人与房子匹配成功的最小总花费。
本题只要把人与房子看成是一个带权二分图,利用 KM 算法进行匹配即可。通过 将每个人到每间房子的花费计算出来,作为边的权值。因为题目要求最小花费,所以需要先将权值取相反数,然后跑 KM,将求得的结果取相反数,就能得到最小花费了。
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e2+2;
struct Node
{
int x, y;
Node() {}
Node(int x, int y): x(x), y(y) {}
};
int n, m;
int nx, ny;
int g[maxn][maxn];
int linker[maxn], lx[maxn], ly[maxn]; //y点中的匹配状态,xy的杠杆值
int slack[maxn]; //新加边的最小差值
bool visx[maxn], visy[maxn];
vector<Node> house, man;
void read()
{
house.clear();
man.clear();
char c;
for(int i = 0; i < n; ++i)
for(int j = 0; j < m; ++j)
{
g[i][j] = -INF;
cin >> c;
if(c == 'H')
house.push_back(Node(i, j));
else if(c == 'm')
man.push_back(Node(i, j));
}
}
bool dfs(int x)
{
visx[x] = 1;
for(int i = 0; i < ny; ++i)
{
if(visy[i]) //每一轮匹配 右图每个点只访问一次
continue;
int t = lx[x] + ly[i] - g[x][i]; //差值
if(!t)
{
visy[i] = 1;
if(linker[i] == -1 || dfs(linker[i])) //找到一个未匹配的点,形成增广路
{
linker[i] = x;
return 1;
}
}
else
slack[i] = min(slack[i], t); //更新最小差值
}
return 0;
}
int KM()
{
memset(linker, -1, sizeof(linker)); //无匹配
memset(ly, 0, sizeof(ly)); //右图各点期望值为0
for(int i = 0; i < nx; ++i)
{
lx[i] = -INF;
for(int j = 0; j < ny; ++j)
lx[i] = max(lx[i], g[i][j]); //lx设置为连接的边权的最大值,即期望的最大值
}
for(int i = 0; i < nx; ++i)
{
memset(slack, INF, sizeof(slack));
while(1)
{
memset(visx, 0, sizeof(visx));
memset(visy, 0, sizeof(visy));
if(dfs(i)) //形成了增广路,匹配成功
break;
//否则,新加边使得能够匹配
int d = INF; //计算d值,d为当前加上一条边,左图需要减少的最小的期望值
for(int j = 0; j < ny; ++j)
if(!visy[j])
d = min(d, slack[j]); //求得最小值
for(int j = 0; j < nx; ++j)
if(visx[j]) //在访问过的点中求
lx[j] -= d; //左图减去最小的期望差值
for(int j = 0; j < ny; ++j)
{
if(visy[j]) //在访问过的点中求
ly[j] += d; //右图加上最小的期望差值
else
slack[j] -= d; //左图期望降低了,那么差值就变小了
}
}
}
int ret = 0;
for(int i = 0; i < ny; ++i)
if(~linker[i]) //有匹配
ret += g[linker[i]][i]; //加上权值
return ret;
}
void solve()
{
nx = man.size();
ny = house.size();
for(int i = 0; i < nx; ++i)
for(int j = 0; j < ny; ++j)
{
Node mt = man[i], ht = house[j];
g[i][j] = -(abs(mt.y-ht.y) + abs(mt.x-ht.x));
}
cout << -KM() << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
while(cin >> n >> m && n && m)
{
read();
solve();
}
return 0;
}