您的位置:首页 > 其它

[省选前题目整理][LA 4043]Ants(二分图最小权匹配)

2015-03-27 08:43 465 查看

题目链接

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=14347

题目大意

有nn个白点、nn个黑点,要在平面中连nn条边,保证每条边连接的是一个白点和一个黑点,且任意两条边不相交,求出一个合法方案。

思路

我们可以将所有的白点看成xx侧,把所有的黑点看成yy侧,每个白点和所有的黑点连边,权值为这两点之间的欧几里得距离,然后跑KM算法,或者再创建超级源点和超级汇点,跑费用流,最终均可得到这个二分图的最小权匹配,这就是答案。

为什么这个思路是正确的呢?我们可以画个图看看:



考虑如上图左的情况,两个白点和两个黑点连边,两个边有交叉,如果我们把这两个边换成不交叉的边(如上图右),则这两个边的权值之和必然减少,正确性显然。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <cmath>

#define MAXV 300
#define MAXE 60000
#define INF 1e9

using namespace std;

int S,T;

struct Edge
{
int u,v,cap,next;
double w;
}edges[MAXE];

int head[MAXV],nCount=0;

void AddEdge(int U,int V,int C,double W)
{
edges[++nCount].u=U;
edges[nCount].v=V;
edges[nCount].cap=C;
edges[nCount].w=W;
edges[nCount].next=head[U];
head[U]=nCount;
}

void add(int U,int V,int C,double W)
{
AddEdge(U,V,C,W);
AddEdge(V,U,0,-W);
}

int pre[MAXV],q[MAXE];
double dist[MAXV];
bool inQueue[MAXV];

bool SPFA()
{
memset(inQueue,false,sizeof(inQueue)); //!!!!!
memset(pre,-1,sizeof(pre));
for(int i=0;i<MAXV;i++) dist[i]=INF;
int h=0,t=1;
q[h]=S;
dist[S]=0;
inQueue[S]=true;
while(h<t)
{
int u=q[h++];
inQueue[u]=false;
for(int p=head[u];p!=-1;p=edges[p].next)
{
int v=edges[p].v;
if(dist[u]+edges[p].w<dist[v]&&edges[p].cap)
{
dist[v]=dist[u]+edges[p].w;
pre[v]=p;
if(!inQueue[v])
{
inQueue[v]=true;
q[t++]=v;
}
}
}
}
return pre[T]!=-1;
}

double MCMF()
{
double cost=0;
while(SPFA())
{
if(dist[T]>=INF) break;
int flow=INF;
for(int p=pre[T];p!=-1;p=pre[edges[p].u])
flow=min(flow,edges[p].cap);
for(int p=pre[T];p!=-1;p=pre[edges[p].u])
{
edges[p].cap-=flow;
edges[p^1].cap+=flow;
}
cost+=flow*dist[T];
}
return cost;
}

struct Point
{
double x,y;
}white[MAXV],black[MAXV];

double eucliddistance(Point a,Point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

int ans[MAXV];

int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
memset(head,-1,sizeof(head));
nCount=1;
S=MAXV-2,T=MAXV-1;
for(int i=1;i<=n;i++)
{
add(S,i,1,0);
add(i+n,T,1,0);
}
for(int i=1;i<=n;i++)
scanf("%lf%lf",&white[i].x,&white[i].y);
for(int i=1;i<=n;i++)
scanf("%lf%lf",&black[i].x,&black[i].y);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
add(i,j+n,1,eucliddistance(white[i],black[j])); //!!!!!!
MCMF();
for(int i=1;i<=n;i++)
for(int j=head[i];j!=-1;j=edges[j].next)
{
if(!edges[j].cap&&edges[j].v>n)
{
ans[i]=edges[j].v-n;
break;
}
}
for(int i=1;i<=n;i++)
printf("%d\n",ans[i]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: