【KM】POJ3565[Ants]题解

来源:互联网 发布:马来西亚华人知乎 编辑:程序博客网 时间:2024/06/04 22:30

题目概述

在平面直角坐标系中有n个蚂蚁窝和n个苹果树(都是点),保证没有三点共线,一个蚂蚁窝只能匹配一个苹果树,一个苹果树也只能匹配一个蚂蚁窝。求一个匹配,使得没有任意两条连线是相交的。

解题报告

把苹果树看成X集合,蚂蚁看成Y集合,那么这显然是一张二分图。但是如何满足不交叉?我们会发现,交叉肯定不存在距离最优解中,因为:
这里写图片描述
左图还不如右图。

所以我们刷出最佳完美匹配,此时的匹配就是一组满足的解。
需要注意的是,KM算法求最小和求最大不是很一样,所以我们可以把边权变成相反数,这样刷最小就等同于刷最大,最后答案取相反数就行了(虽然这道题不用刷答案)。

示例程序

#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>using namespace std;const int maxn=100;int n,who[maxn+5];double Lx[maxn+5],Ly[maxn+5];double MINs[maxn+5],cst[maxn+5][maxn+5];bool S[maxn+5],T[maxn+5];struct Point {int x,y;};Point a[maxn+5],t[maxn+5];int sqr(int x) {return x*x;}double getdis(Point &a,Point &b) {return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));}bool Find(int x){    S[x]=true;    for (int y=1;y<=n;y++) if (!T[y])    {        double s=Lx[x]+Ly[y]-cst[x][y];        if (fabs(s)<1e-10)        {            T[y]=true;            if (!who[y]||Find(who[y])) {who[y]=x;return true;}        } else        MINs[y]=min(MINs[y],s);    }    return false;}void KM(){    for (int i=1;i<=n;i++) Lx[i]=-(1e100);    for (int i=1;i<=n;i++)    for (int j=1;j<=n;j++)        Lx[i]=max(Lx[i],cst[i][j]);    memset(Ly,0,sizeof(Ly));    memset(who,0,sizeof(who));    for (int now=1;now<=n;now++)    {        for (int i=1;i<=n;i++) MINs[i]=1e100;        while (true)        {            memset(S,0,sizeof(S));memset(T,0,sizeof(T));            if (Find(now)) break;            double MIN=1e100;            for (int i=1;i<=n;i++) if (!T[i]) MIN=min(MIN,MINs[i]);            for (int i=1;i<=n;i++)            {                if (S[i]) Lx[i]-=MIN;                if (T[i]) Ly[i]+=MIN; else                MINs[i]-=MIN;            }        }    }}int main(){    freopen("program.in","r",stdin);    freopen("program.out","w",stdout);    scanf("%d",&n);    for (int i=1;i<=n;i++) scanf("%d%d",&t[i].x,&t[i].y);    for (int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);    for (int i=1;i<=n;i++)    for (int j=1;j<=n;j++)        cst[i][j]=-getdis(a[i],t[j]);    KM();    for (int i=1;i<=n;i++) printf("%d\n",who[i]);    return 0;}
原创粉丝点击