思路:
将ant与tree之间用距离来做权值,求最小权匹配就可以了。可以想到,如果有两条线段相交,那么将这两个线段交换一个顶点,使其不相交,其权值和一定会更小。
就像斜边永远比直角边长一样的道理。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define Maxn 110 #define Eps 0.000001 using namespace std; int sx[Maxn],sy[Maxn],match[Maxn],n; double lx[Maxn],ly[Maxn],weight[Maxn][Maxn],slack[Maxn]; struct Point{ double x,y; }tree[Maxn],ant[Maxn]; double Dis(Point a,Point b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } int dfs(int u) { int i; sx[u]=1; for(i=1;i<=n;i++) { if(!sy[i]&&(lx[u]+ly[i]-weight[u][i]<Eps)) { sy[i]=1; if(match[i]==-1||dfs(match[i])) { match[i]=u; return 1; } } if(!sy[i]&&slack[i]>lx[u]+ly[i]-weight[u][i]) slack[i]=lx[u]+ly[i]-weight[u][i]; } return 0; } int bestmatch() { int i,j; for(i=1;i<=n;i++) { lx[i]=-10000000; ly[i]=0; for(j=1;j<=n;j++) { weight[i][j]=-weight[i][j]; lx[i]=max(lx[i],weight[i][j]); } } memset(match,-1,sizeof(match)); //cout<<n<<endl; for(i=1;i<=n;i++) { for(j=0;j<=n;j++) slack[j]=10000000; while(1) { memset(sx,0,sizeof(sx)); memset(sy,0,sizeof(sy)); if(dfs(i)) break; double dx=10000000; for(j=1;j<=n;j++) if(!sy[j]) dx=min(dx,slack[j]);//每次找的都是不在交错路径上的点,故slack[i]==0的情况不会被遍历 for(j=1;j<=n;j++) { if(sx[j]) lx[j]-=dx; if(sy[j]) ly[j]+=dx; else slack[j]-=dx;//由于X(i)已经更新过了,故每个slack[i]要减去dx,否则下次循环就会多减 } } } int ans=0; for(i=1;i<=n;i++) ans+=weight[match[i]][i]; return -ans; } int main() { int i,j; while(scanf("%d",&n)==1) { for(i=1;i<=n;i++) scanf("%lf%lf",&ant[i].x,&ant[i].y); for(i=1;i<=n;i++) scanf("%lf%lf",&tree[i].x,&tree[i].y); for(i=1;i<=n;i++) for(j=1;j<=n;j++) weight[i][j]=Dis(tree[i],ant[j]); bestmatch(); for(i=1;i<=n;i++) printf("%d ",match[i]); } return 0; }