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

题意:

  有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=;
int antx[N], anty[N], treex[N], treey[N];
double g[N][N]; //距离 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[N], Ly[N], slack[N];
int girl[N];
int S[N], T[N]; bool DFS(int x)
{
S[x]=true;
for(int i=; i<=n; i++)
{
if(T[i]) continue;
double tmp=Lx[x]+Ly[i]-g[x][i];
if(tmp<1e-)
{
T[i]=true;
if(girl[i]== || 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=; i<=n; i++) //初始化工作
{
girl[i]=;
Lx[i]=-1e19;
Ly[i]=0.0;
for(int j=; j<=n; j++)
Lx[i]=max(Lx[i], g[i][j]);
}
for(int i=; i<=n; i++) //对于每个树
{
for(int j=; j<=n; j++) slack[j]=1e19;
while()
{
memset(S, , sizeof(S));
memset(T, , sizeof(T));
if( DFS(i) ) break; //找到匹配的蚂蚁 double d=1e19;
for(int j=; j<=n; j++) //找最小D
{
if(!T[j] && d>slack[j])
d=slack[j];
} for(int j=; j<=n; j++) //更新树
{
if(S[j])
Lx[j]-=d;
} for(int j=; j<=n; j++) //更新蚂蚁
{
if(T[j]) Ly[j]+=d;
else slack[j]-=d;
}
}
}
} int main()
{
freopen("input.txt", "r", stdin);
int k=;
while(~scanf("%d",&n))
{
if(k) printf("\n");
k++;
for(int i=; i<=n; i++)
scanf("%d%d", &antx[i], &anty[i]); //ant
for(int i=; i<=n; i++)
scanf("%d%d", &treex[i], &treey[i]); //apple tree for(int i=; i<=n; i++)
for(int j=; j<=n; j++)
g[i][j]=-dis(i,j); KM(n);
for(int i=; i<=n; i++)
printf("%d\n", girl[i]); //ans为girl
}
return ;
}

AC代码

上一篇:Java 基础 面向对象和抽象类


下一篇:oracle having sum group by 详解