蒟蒻来写一篇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\n",match[i]); }//输出结果 } return 0; }