蒟蒻来写一篇KM算法的题解。
首先,如果所有线段不相交,那么线段长度之和一定最小。证明很简单,在这里不再赘述。(提示:三角形两边之和大于第三边)
那么这道题就转化为了一道带权二分图匹配问题,可以使用费用流求解。又由于黑白点数相同,所以带权最大匹配一定是完备匹配,故可以用KM算法求解。
若不会KM算法,请到这里学习。
然后就把KM算法的板子改一下,在每个黑点与白点之间连一条权值为距离的边,就可以A了。
代码:
#include<bits/stdc++.h>
using namespace std;
const double INF=1e9;
double Map[101][101],la[101],lb[101],va[101],vb[101],upd[101],delta;//一定要开double
int match[101],n;
bool dfs(int x){
va[x]=1;
for(int i=1;i<=n;i++){
if(!vb[i]){
if(fabs(la[x]+lb[i]-Map[x][i])<=0.0001){//注意精度
vb[i]=1;
if(!match[i]||dfs(match[i])){
match[i]=x;
return true;
}
}
else{
upd[i]=min(upd[i],la[x]+lb[i]-Map[x][i]);
}
}
}
return false;
}
void KM(){//KM算法,不多解释
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],Map[i][j]);
}
}
for(int i=1;i<=n;i++){
while(1){
memset(va,0,sizeof(va));
memset(vb,0,sizeof(vb));
for(int i=1;i<=n;i++){
upd[i]=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;
}
}
}
}
void init(){
memset(Map,0,sizeof(Map));
memset(match,0,sizeof(match));
}
int main() {
double x1[101],x2[101],y1[101],y2[101];
while(scanf("%d",&n)!=EOF){
init();
for(int i=1;i<=n;i++){
scanf("%lf %lf",&x2[i],&y2[i]);
}
for(int i=1;i<=n;i++){
scanf("%lf %lf",&x1[i],&y1[i]);
}
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
Map[i][j]=-double(sqrt((x1[i]-x2[j])*(x1[i]-x2[j])+(y1[i]-y2[j])*(y1[i]-y2[j])));
}
}//计算所有黑白点之间的距离,并连边
KM();//KM求带权匹配
for(int i=1;i<=n;i++){
printf("%d
",match[i]);
}//输出结果
}
return 0;
}