UVA1411 Ants

算法竞赛 算法-KM算法
编辑文章

题意

给出黑点白点各 $n(\le 100)$ 个,要求一个黑点连接一个白点,并且所有线段都不相交。

题解

所有线段不相交 $\rightarrow$ 两两连最近的点 。

那么把距离变成负数就是KM算法裸题了,但这道题有个神奇的坑点:如果在 find() 里更新顶标的偏移值 d ,就会T;而改在外面更新就A了。我也不知道为什么,看来以后得改下KM算法的写法了。

#include<bits/stdc++.h>
#define eps 1e-4
#define pdd pair<double,double>
#define fi first
#define se second

using namespace std;

const int N=105;
int n,mch[N];
pdd a[N],b[N];
bool lv[N],rv[N];
double d,l[N],r[N],dis[N][N];

inline double getDis(pdd x,pdd y)
{
    double dx=x.first-y.first,dy=x.second-y.second;
    return sqrt(dx*dx+dy*dy);
}

bool find(int x)
{
    lv[x]=1;
    for (int y=1;y<=n;y++)
    {
        if (rv[y]) continue;
        if (fabs(l[x]+r[y]-dis[x][y])<eps)
        {
            rv[y]=1;
            if (!mch[y] || find(mch[y])) return mch[y]=x,1;
        }
        //else d=min(d,l[x]+r[y]-dis[x][y]); 如果在这更新就会T
    }
    return 0;
}

inline void km()
{
    memset(l,0,sizeof(l));
    memset(r,0,sizeof(r));
    memset(mch,0,sizeof(mch));
    for (int i=1;i<=n;i++)
    {
        for (;;)
        {
            memset(lv,0,sizeof(lv));
            memset(rv,0,sizeof(rv));
            d=1e9;
            if (find(i)) break;
            for (int i=1;i<=n;i++) //在外面更新
            {
                if (!lv[i]) continue;
                for (int j=1;j<=n;j++)
                {
                    if (rv[j]) continue;
                    d=min(d,l[i]+r[j]-dis[i][j]);
                }
            }
            for (int j=1;j<=n;j++)
            {
                if (lv[j]) l[j]-=d;
                if (rv[j]) r[j]+=d;
            }
        }
    }
}

signed main()
{
    while (scanf("%d",&n)!=EOF)
    {
        for (int i=1;i<=n;i++) scanf("%lf%lf",&a[i].fi,&a[i].se);
        for (int i=1;i<=n;i++)
        {
            scanf("%lf%lf",&b[i].fi,&b[i].se);
            for (int j=1;j<=n;j++) dis[i][j]=-getDis(b[i],a[j]);
        }
        km();
        for (int i=1;i<=n;i++) printf("%d\n",mch[i]);
    }
    return 0;
}

新评论

称呼不能为空
邮箱格式不合法
网站格式不合法
内容不能为空