题目大意:
平面上共有2*N个点,N个是白点,N个是黑点。对于每个白点,找到一个黑点,把二者用线连起来,要求最后所有线段都不想交,求一种方案。
满足总路程之和最小的方案一定不相交
详见 https://blog.csdn.net/lianai911/article/details/44835659
可以建立一个二部图,然后用KM跑最小权匹配,KM跑最小权其实就是把各个边上的权值取反然后跑KM,最后的结果再取反就是最小权。
此题只需要取反后跑一边KM,然后输出匹配边上的点即可
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#define mem(a, b) memset(a, b, sizeof a)
#define eps 1e-8
const int inf = 0x3f3f3f3f;
using namespace std;
const int N = 110;
struct p {
int x, y;
p() {
}
p(int x, int y) {
this->x = x;
this->y = y;
}
};
p black[N], white[N];
double calculate(p& a, p& b) {
double dx = ((double)a.x - b.x) * 1.0;
double dy = ((double)a.y - b.y) * 1.0;
return sqrt(dx * dx + dy * dy);
}
int n;
int match[N];
double w[N][N];
bool va[N], vb[N];
double la[N], lb[N];
double slack[N];
int res[N];
bool dfs(int x) {
va[x] = 1;
for (int i = 1; i <= n; i++) {
if (!vb[i]) {
double t = la[x] + lb[i] - w[x][i];
if (fabs(t) < eps) {
// ????
vb[i] = 1;
if (match[i] == -1 || dfs(match[i])) {
match[i] = x;
return 1;
}
}
else if (slack[i] > t)slack[i] = t;
}
}
return 0;
}
void km() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
w[i][j] = -calculate(black[i], white[j]);
}
}
mem(match, -1);
for (int i = 1; i <= n; i++) {
la[i] = -1e12;
lb[i] = 0;
for (int j = 1; j <= n; j++) {
if (la[i] < w[i][j])la[i] = w[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
slack[j] = 1e12;
}
while (1) {
mem(va, 0);
mem(vb, 0);
if (dfs(i))break;
double d = 1e12;
for (int j = 1; j <= n; j++) {
if (!vb[j] && d > slack[j])d = slack[j];
}
for (int j = 1; j <= n; j++) {
if (va[j])la[j] -= d;
if (vb[j])lb[j] += d;
else slack[j] -= d;
}
}
}
mem(res, -1);
double ans = 0;
for (int i = 1; i <= n; i++) {
res[match[i]] = i;
}
for (int i = 1; i <= n; i++) {
cout << res[i] << "\n";
}
}
int main()
{
ios::sync_with_stdio(0);
while (cin >> n) {
for (int i = 1; i <= n; i++) {
int x, y;
cin >> x >> y;
black[i].x = x;
black[i].y = y;
}
for (int j = 1; j <= n; j++) {
int x, y;
cin >> x >> y;
white[j].x = x;
white[j].y = y;
}
km();
}
return 0;
}