题意:给出若n个白点和n个黑点,要求用n条线段链接并使这些线段不相交
思路:想了许久没想出来,看了刘汝佳的训练指南题解。如果两条线段a1-b1,a2-b2相交,那么他们的距离之和一定大于a1 - b2, a2 - b1,所以如果有相交的线段的话不符合二分图最佳匹配的原则,所以最佳匹配中不会出现线段相交的情况,设初始值为距离的相反数就好了,这样就选择的最佳匹配满足条件,直接KM模板可过
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
const int maxn = 1e2 + 10;
const double INF = 1e12;
const double eps = 1e-6;
using namespace std;
double x[maxn], y[maxn], X[maxn], Y[maxn];
double w[maxn][maxn];
double lx[maxn], ly[maxn];
int from[maxn], n;
bool S[maxn], T[maxn];
double res(double a) {
if(fabs(a) < eps) return 0;
return a;
}
bool match(int i) {
S[i] = true;
for(int j = 1; j <= n; j++) {
if(res(lx[i] + ly[j] - w[i][j]) || T[j]) continue;
T[j] = true;
if(!from[j] || match(from[j])) {
from[j] = i;
return true;
}
}
return false;
}
void update() {
double a = INF;
for(int i = 1; i <= n; i++) if(S[i])
for(int j = 1; j <= n; j++) if(!T[j])
a = min(a, lx[i] + ly[j] - w[i][j]);
for(int i = 1; i <= n; i++) {
if(S[i]) lx[i] -= a;
if(T[i]) ly[i] += a;
}
}
void KM() {
for(int i = 1; i <= n; i++) {
from[i] = 0; lx[i] = ly[i] = 0;
for(int j = 1; j <= n; j++)
lx[i] = max(lx[i], w[i][j]);
}
for(int i = 1; i <= n; i++) {
while(1) {
memset(S, false, sizeof(S));
memset(T, false, sizeof(T));
if(match(i)) break;
else update();
}
}
}
int main() {
while(scanf("%d", &n) != EOF) {
for(int i = 1; i <= n; i++)
scanf("%lf %lf", &x[i], &y[i]);
for(int i = 1; i <= n; i++)
scanf("%lf %lf", &X[i], &Y[i]);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
double dx = x[i] - X[j];
double dy = y[i] - Y[j];
w[j][i] = -sqrt(dx * dx + dy * dy);
}
}
KM();
for(int i = 1; i <= n; i++) {
printf("%d\n", from[i]);
}
printf("\n");
}
return 0;
}