poj3565 ants(KM)

来源:互联网 发布:电磁炮 激光炮 知乎 编辑:程序博客网 时间:2024/05/17 06:14

题目链接:http://poj.org/problem?id=3565
题意: 有 N 个蚂蚁,N 个苹果,要在每个蚂蚁和一个相应的苹果之间连边,问如何给蚂蚁分配苹果,可以使这些边不相交。

分析:
应为在最小权值匹配的情况下满足没有边相交,可以简单证明,假设最小完备匹配中有两条线段AC和BD相交于点E,此时我们可以不连接AC和BD而去连接AD和BC,由于AE+DE>AD和BE+CE>BC,所有我们可以得出新连接的边的权值和一定比原来的边的权值和小,这样就可以得到一种权值和更小的匹配,这原来的匹配是最小带权和的匹配矛盾, 因此最小带权和的匹配中不会出现有两条线段相交的情况。KM算法是求最大权匹配问题,因此将此处的权值取距离的相反数即可。

源代码:

#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>using namespace std;const double INF=9999999999.0;const double eps=1e-5;struct Node{    double x,y;} ant[110],apple[110];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 nx,ny;bool visx[110],visy[110];int linker[110];double lx[110],ly[110];double slack[110];double mp[110][110];bool dfs(int x){    visx[x]=true;    for(int y=1; y<=ny; y++)    {        if(visy[y]) continue;        double tmp=lx[x]+ly[y]-mp[x][y];        if(fabs(tmp)<eps)        {            visy[y]=true;            if(linker[y]==-1 || dfs(linker[y]))            {                linker[y]=x;                return true;            }        }        else if(slack[y]>tmp)            slack[y]=tmp;    }    return false;}void KM(){    memset(linker,-1,sizeof linker);    memset(ly,0,sizeof ly);    for(int i=1; i<=nx; i++)    {        lx[i]=-INF;        for(int j=1; j<=ny; j++)            if(mp[i][j]>lx[i])                lx[i]=mp[i][j];    }    for(int x=1; x<=nx; x++)    {        for(int i=1; i<=ny; i++)            slack[i]=INF;        while(1)        {            memset(visx,0,sizeof visx);            memset(visy,0,sizeof visy);            if(dfs(x)) break;            double d=INF;            for(int i=1; i<=ny; i++)                if(!visy[i] && d>slack[i])                    d=slack[i];            for(int i=1; i<=nx; i++)                if(visx[i])                    lx[i]-=d;            for(int i=1; i<=ny; i++)            {                if(visy[i])                    ly[i]+=d;                else slack[i]-=d;            }        }    }}int main(){    int n;    while(scanf("%d",&n)!=EOF)    {        for(int i=1; i<=n; i++)        {            scanf("%lf%lf",&ant[i].x,&ant[i].y);        }        for(int i=1; i<=n; i++)        {            scanf("%lf%lf",&apple[i].x,&apple[i].y);        }        for(int i=1; i<=n; i++)        {            for(int j=1; j<=n; j++)            {                mp[i][j]=-dis(apple[i],ant[j]);            }        }        nx=ny=n;        KM();        for(int i=1;i<=ny;i++)            printf("%d\n",linker[i]);    }    return 0;}
0 0
原创粉丝点击