POJ 3565 Ants【KM】
题意: 有 N 个蚂蚁,N 个苹果,要在每个蚂蚁和一个相应的苹果之间连边,问如何给蚂蚁分配苹果,可以使这些边不相交。
分析:
应为在最小权值匹配的情况下满足没有边相交,可以简单证明,假设最小完备匹配中有两条线段AC和BD相交于
点E,此时我们可以不连接AC和BD而去连接AD和BC,由于AE+DE>AD和BE+CE>BC,所有我们可以得出新
连接的边的权值和一定比原来的边的权值和小,这样就可以得到一种权值和更小的匹配,这原来的匹配是最小带
权和的匹配矛盾, 因此最小带权和的匹配中不会出现有两条线段相交的情况。
code:
#include<stdio.h> #include<string.h> #include<math.h> #define clr(x)memset(x,0,sizeof(x)) const double INF=99999999.0; double map[102][102]; struct node { double x,y; }an[102],ap[102]; int link[102]; int sx[102],sy[102]; double lx[102],ly[102]; int n; bool judge(int v,int i) { double esp; esp=0.0001; double r=lx[v]+ly[i]; double z=map[v][i]; if(abs(r-z)<=esp) return true; else return false; } int find(int x) { sx[x]=1; int i; for(i=1;i<=n;i++) if(!sy[i]&&judge(x,i)) { sy[i]=1; if(link[i]==0||find(link[i])) { link[i]=x; return 1; } } return 0; } void KM() { clr(ly); int v,i,j; double dmin; for(i=1;i<=n;i++) for(j=1;j<=n;j++) map[i][j]=-map[i][j]; for(i=1;i<=n;i++) lx[i]=INF; clr(link); for(v=1;v<=n;v++) { clr(sx); clr(sy); while(1) { if(find(v)) break; dmin=INF; for(i=1;i<=n;i++) if(sx[i]) for(j=1;j<=n;j++) if(!sy[j]&&lx[i]+ly[j]-map[i][j]<dmin) dmin=lx[i]+ly[j]-map[i][j]; for(i=1;i<=n;i++) { if(sx[i]){ lx[i]-=dmin; sx[i]=0; } if(sy[i]){ ly[i]+=dmin; sy[i]=0; } } } } } double dis(node a,node b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } int main() { int i,j; while(scanf("%d",&n)!=EOF) { for(i=1;i<=n;i++) scanf("%lf%lf",&an[i].x,&an[i].y); for(i=1;i<=n;i++) scanf("%lf%lf",&ap[i].x,&ap[i].y); for(i=1;i<=n;i++) for(j=1;j<=n;j++) map[i][j]=dis(ap[i],an[j]); KM(); for(i=1;i<=n;i++) printf("%d\n",link[i]); } return 0; }