zoukankan      html  css  js  c++  java
  • poj3565Ants——KM算法

    题目:http://poj.org/problem?id=3565

    首先,我们神奇地发现,没有相交边的匹配可以转化为距离和最小的匹配,所以可以使用KM算法求带权匹配;

    要求的是距离和最小,所以把边权转化成负值来求最大;

    KM算法有点难理解,看了许多博客,总算朦胧懂了:

    首先,每个点有一个“顶标”,用来计算边权和,起初所有左部点的顶标都为相连边的最大边权,右部点的为0;

    两个点匹配成功,仅当其顶标和等于其边权(所以可以通过调整顶标来控制边权);

    一次dfs没有成功返回0时,我们已经记录下它经过的所有点,这些点都满足“其顶标和等于其边权”条件,组成了一个相等子图;

    然而此时的相等子图无法使遍历到的这个左部点得到匹配,所以需要调整顶标;

    可知最终的匹配中顶标和等于边权和,为了让顶标和下降最少而又有可能使这个点得到匹配,我们在所有相等子图中的左部点和不在相等子图中的右部点之间找到距离满足条件最小的差值,这样根据它调整顶标后,顶标和下降最少,而又至少会多出这一组可行的点加入相等子图;

    这样重复进行,直到这个左部点匹配成功;

    最后匹配的权值和是现在的顶标和,这个顶标和比起一开始我们期望的和(每个左部点顶标为其所连最大的边)有所下调,但我们保证每次下调量最少,所以得到最优匹配。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    double const inf=999999.999999,eps=1e-7;
    int const MAXN=105;
    int n,pre[MAXN],ans[MAXN],xa[MAXN],xb[MAXN],ya[MAXN],yb[MAXN];
    double dis[MAXN][MAXN],la[MAXN],lb[MAXN];
    bool va[MAXN],vb[MAXN];
    bool dfs(int x)
    {
        va[x]=1;
        for(int i=1;i<=n;i++)
            if(!vb[i]&&fabs(la[x]+lb[i]-dis[x][i])<eps)
            {
                vb[i]=1;
                if(!pre[i]||dfs(pre[i]))
                    {pre[i]=x;return 1;}
            }
        return 0;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d%d",&xa[i],&ya[i]);
        for(int i=1;i<=n;i++)
            scanf("%d%d",&xb[i],&yb[i]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                dis[i][j]=-sqrt(1.0*(xa[i]-xb[j])*(xa[i]-xb[j])+1.0*(ya[i]-yb[j])*(ya[i]-yb[j]));
        memset(la,-0x3f,sizeof la);
        for(int i=1;i<=n;i++)
        {
            la[i]=dis[i][1];
            for(int j=2;j<=n;j++)
                la[i]=max(la[i],dis[i][j]);
        }
        for(int i=1;i<=n;i++)
        {
            while(1)
            {
                memset(va,0,sizeof va);
                memset(vb,0,sizeof vb);
                if(dfs(i))break;
                double tmp=inf;
                for(int i=1;i<=n;i++) if(va[i])
                    for(int j=1;j<=n;j++) if(!vb[j])
                        tmp=min(tmp,la[i]+lb[j]-dis[i][j]);
                for(int i=1;i<=n;i++)
                {
                    if(va[i])la[i]-=tmp;
                    if(vb[i])lb[i]+=tmp;
                }
            }
        }
        for(int i=1;i<=n;i++)
            ans[pre[i]]=i;
        for(int i=1;i<=n;i++)
            printf("%d
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    hdu 3613 Best Reward 扩展kmp
    hdu 4333 Revolving Digits 扩展kmp
    poj 1904 King's Quest 强连通
    hdu 3068 最长回文 manacher
    Codeforces Round #243 (Div. 2) C. Sereja and Swaps
    poj 3680 Intervals 费用流
    两个kmp hdu 2594 & hdu 2087
    hdu 3336 count the string
    Arcgis安装要素
    JS关闭窗口而不提示
  • 原文地址:https://www.cnblogs.com/Zinn/p/8870817.html
Copyright © 2011-2022 走看看