您的位置:首页 > 其它

poj3565 ants(KM)

2016-08-06 19:18 197 查看
题目链接: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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  poj3565 ants KM