Link
Solve
题目要求所有线段都不相交,等价于让每条线段的长度之和最小。
如果第(i_1)个白点连第(j_1)个黑点,第(i_2)个白点连接第(j_2)个黑点,并且线段((i_1,j_1))和((i_2,j_2))相交,那么交换一下,让(i_1)连(j_2),让(j_1)连(i_2),这样就不会交叉,并且有三角形两边和大于第三边可知,两条线段的长度和一定变小。
所以就可以把(N)个白点看作左部点,(N)个端点看作右部端点,让第(i)个白点和第(j)个黑点连边,边权(w(i,j))就是他们在平面上的距离
用(KM)算法求二分图带权最小匹配时,我们把所有边权(w(i,j))取为相反数,然后转化为求二分图带权最大匹配,最后把答案(-ans)作为答案即可。
Code
#include<bits/stdc++.h>
using namespace std;
const int maxn=105,INF=1<<30;
int N,match[maxn];
bool va[maxn],vb[maxn];
double X_w[maxn],la[maxn],lb[maxn],Y_w[maxn],X_b[maxn],Y_b[maxn],w[maxn][maxn],delta,upd[maxn],ans;
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
bool DFS(int x){
va[x]=1;
for(int y=1;y<=N;y++)
if(!vb[y]){
if(abs(la[x]+lb[y]-w[x][y])<1e-7){
vb[y]=1;
if(!match[y]||DFS(match[y])){
match[y]=x;
return true;
}
}
else upd[y]=min(upd[y],la[x]+lb[y]-w[x][y]);
}
return false;
}
void KM(){
memset(match,0,sizeof match);
for(int i=1;i<=N;i++){
la[i]=-INF;
lb[i]=0;
for(int j=1;j<=N;j++)la[i]=max(la[i],w[i][j]);
}
for(int i=1;i<=N;i++)
while(true){
memset(va,0,sizeof va);
memset(vb,0,sizeof vb);
for(int j=1;j<=N;j++)upd[j]=INF;
if(DFS(i))break;delta=INF;
for(int j=1;j<=N;j++)
if(!vb[j])delta=min(delta,upd[j]);
for(int j=1;j<=N;j++){
if(va[j]) la[j]-=delta;
if(vb[j]) lb[j]+=delta;
}
}
}
int main(){
freopen("UVA1411.in","r",stdin);
freopen("UVA1411.out","w",stdout);
while(scanf("%d",&N)!=EOF){
for(int i=1;i<=N;i++)
scanf("%lf%lf",&X_w[i],&Y_w[i]);
for(int i=1;i<=N;i++)
scanf("%lf%lf",&X_b[i],&Y_b[i]);
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
w[j][i]=-sqrt((X_w[i]-X_b[j])*(X_w[i]-X_b[j])+(Y_w[i]-Y_b[j])*(Y_w[i]-Y_b[j]));
KM();
for(int i=1;i<=N;i++)printf("%d
",match[i]);
}
return 0;
}