您的位置:首页 > 其它

UVALive 4043 Ants 蚂蚁(二分图最佳完美匹配,KM算法)

2015-08-10 22:57 316 查看
题意:

  有n个蚂蚁n棵树,蚂蚁与树要配对,在配对成功的一对之间连一条线段,要求所有线段不能相交。按顺序输出蚂蚁所匹配的树。

思路:

  这个题目真是技巧啊,不能用贪心来为每个蚂蚁选择最近的树,这样很可能是相交了的。

  整体最优能让每条线段不相交,证明:

  假设a1-b1与a2-b2相交。则dis(a1,b1)+dis(a2,b2)>=dis(a1,b2)+dis(a2,b1)。如果我们所决定的最优匹配是按照整体距离最短来匹配的,那么dis(a1,b1)+dis(a2,b2)必定小于dis(a1,b2)+dis(a2,b1),否则,与最优矛盾。推广到整个图就是匹配图中任意两个点都是最优的,否则我们一定可以用更优的方式来替代他们。而整体最优靠的是KM算法。注意到,本题是完全二分图。

  Tips:要选的是整体权值最小,只需要将边权置为距离相反数再跑KM算法即可。

#include <bits/stdc++.h>
using namespace std;
const int N=110;
int antx
, anty
, treex
, treey
;
double g

;     //距离

inline double dis(int a,int b)
{
return sqrt((treex[a]-antx[b])*(treex[a]-antx[b])+(treey[a]-anty[b])*(treey[a]-anty[b]));
}

int n;
double Lx
, Ly
, slack
;
int girl
;
int S
, T
;

bool DFS(int x)
{
S[x]=true;
for(int i=1; i<=n; i++)
{
if(T[i])    continue;
double tmp=Lx[x]+Ly[i]-g[x][i];
if(tmp<1e-6)
{
T[i]=true;
if(girl[i]==0 || DFS(girl[i]))
{
girl[i]=x;
return true;
}
}
else if(slack[i]>tmp)
slack[i]=tmp;
}
return false;
}

void KM(int n)
{
for(int i=1; i<=n; i++) //初始化工作
{
girl[i]=0;
Lx[i]=-1e19;
Ly[i]=0.0;
for(int j=1; j<=n; j++)
Lx[i]=max(Lx[i], g[i][j]);
}
for(int i=1; i<=n; i++) //对于每个树
{
for(int j=1; j<=n; j++) slack[j]=1e19;
while(1)
{
memset(S, 0, sizeof(S));
memset(T, 0, sizeof(T));
if( DFS(i) )    break;      //找到匹配的蚂蚁

double d=1e19;
for(int j=1; j<=n; j++) //找最小D
{
if(!T[j] && d>slack[j])
d=slack[j];
}

for(int j=1; j<=n; j++) //更新树
{
if(S[j])
Lx[j]-=d;
}

for(int j=1; j<=n; j++) //更新蚂蚁
{
if(T[j])    Ly[j]+=d;
else        slack[j]-=d;
}
}
}
}

int main()
{
freopen("input.txt", "r", stdin);
int k=0;
while(~scanf("%d",&n))
{
if(k)   printf("\n");
k++;
for(int i=1; i<=n; i++)
scanf("%d%d", &antx[i], &anty[i]);      //ant
for(int i=1; i<=n; i++)
scanf("%d%d", &treex[i], &treey[i]);    //apple tree

for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
g[i][j]=-dis(i,j);

KM(n);
for(int i=1; i<=n; i++)
printf("%d\n", girl[i]);    //ans为girl
}
return 0;
}


AC代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: