POJ_3565_Ants

题意:给出N个白点和N个黑点,要求用N条不相交的线段把它们连接起来,其中每条线段恰好连接一个白点和一个黑点,每个点恰好连接到一条线段。

分析:因为有结点黑白两色,我们不难想到构造一个二分图,其中每个白点对应一个X结点,每个黑点对应一个Y结点,每个黑点和每个白点相连,权值等于二者的欧几里德距离。建模后最佳完美匹配就是问题的解。为什么呢?假设在最佳完美匹配中有两条线段a1-b1与a2-b2相交,那么dist(a1,b1)+dist(a2,b2)一定大于dist(a1,b2)+dist(a2,b1),因此如果把这两条改成a1-b2和a2-b1后总长度会变少,与最佳二字矛盾。

注意:KM算法是求权值和最大的,故需要将距离边成负数即可。并且输入坐标值好像是浮点的。

参考自:http://www.cnblogs.com/arbitrary/archive/2013/02/27/2936008.html

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define Del(x,y) memset(x,y,sizeof(x))
#define N 105
#define INF 999999999
struct Point
{
    double x,y;
} point[N*2];

double dis(Point a,Point b)
{
    return sqrt((double)((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
}

double lx[N],ly[N];    //顶点标号
int link[N];       //存放与T点集连结的S点集里的点
int S[N],T[N];        //visit,是否属于相等子图
double w[N][N];
int n;

bool match(int i)          //匈牙利
{
    S[i]=true;
    for(int j=1;j<=n;j++)
    {
        if(abs(lx[i]+ly[j]-w[i][j])<10e-5&&!T[j])
        {
            T[j]=true;
            if(link[j]==0||match(link[j]))
            {
                link[j]=i;
                return true;
            }
        }
    }
    return false;
}

void update()                  //更新顶点标号
{
    double a=INF;
    for(int i=1;i<=n;i++)
        if(S[i])
        for(int j=1;j<=n;j++)
            if(!T[j])
                a=min(a,lx[i]+ly[j]-w[i][j]);
    for(int i=1;i<=n;i++)
    {
        if(S[i]) lx[i]-=a;   //S集里的点-a
        if(T[i]) ly[i]+=a;   //T集里的点+a
    }              //其余所有点不变
}

void KM()
{
    for(int i=1; i<=n; i++)
    {
        link[i]=lx[i]=ly[i]=0;
        for(int j=1; j<=n; j++)
            lx[i]=max(lx[i],w[i][j]);
    }
    for(int i=1; i<=n; i++)
        for(;;)
        {
            Del(S,0);
            Del(T,0);
            if(match(i))break;
            else update();
        }
}

int ans[N];

int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n*2; i++)
        scanf("%lf%lf",&point[i].x,&point[i].y);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            w[i][j]=-dis(point[i],point[j+n]);
    KM();
    for(int i=1;i<=n;i++)
        ans[link[i]]=i;
    for(int i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    return 0;
}
时间: 2024-11-06 03:49:48

POJ_3565_Ants的相关文章