题解 UVA1411 【Ants】

Link

UVA1411 Ants

Solve

题目要求所有线段都不相交,等价于让每条线段的长度之和最小。

如果第(i_1)个白点连第(j_1)个黑点,第(i_2)个白点连接第(j_2)个黑点,并且线段((i_1,j_1))((i_2,j_2))相交,那么交换一下,让(i_1)(j_2),让(j_1)(i_2),这样就不会交叉,并且有三角形两边和大于第三边可知,两条线段的长度和一定变小。

所以就可以把(N)个白点看作左部点,(N)个端点看作右部端点,让第(i)个白点和第(j)个黑点连边,边权(w(i,j))就是他们在平面上的距离

(KM)算法求二分图带权最小匹配时,我们把所有边权(w(i,j))取为相反数,然后转化为求二分图带权最大匹配,最后把答案(-ans)作为答案即可。

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=105,INF=1<<30;
int N,match[maxn];
bool va[maxn],vb[maxn];
double X_w[maxn],la[maxn],lb[maxn],Y_w[maxn],X_b[maxn],Y_b[maxn],w[maxn][maxn],delta,upd[maxn],ans;
inline int read(){
	int ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}
bool DFS(int x){
	va[x]=1;
	for(int y=1;y<=N;y++)
		if(!vb[y]){
			if(abs(la[x]+lb[y]-w[x][y])<1e-7){
				vb[y]=1;
				if(!match[y]||DFS(match[y])){
					match[y]=x;
					return true;
				}
			}
			else upd[y]=min(upd[y],la[x]+lb[y]-w[x][y]); 
		}
	return false;
}

void KM(){
	memset(match,0,sizeof match);
	for(int i=1;i<=N;i++){
		la[i]=-INF;
		lb[i]=0;
		for(int j=1;j<=N;j++)la[i]=max(la[i],w[i][j]);
	}
	for(int i=1;i<=N;i++)
		while(true){
			memset(va,0,sizeof va);
			memset(vb,0,sizeof vb);
			for(int j=1;j<=N;j++)upd[j]=INF;
			if(DFS(i))break;delta=INF;
			for(int j=1;j<=N;j++)
				if(!vb[j])delta=min(delta,upd[j]);
			for(int j=1;j<=N;j++){
				if(va[j]) la[j]-=delta;
				if(vb[j]) lb[j]+=delta;
			}
		}
}
int main(){
	freopen("UVA1411.in","r",stdin);
	freopen("UVA1411.out","w",stdout);
	while(scanf("%d",&N)!=EOF){
		for(int i=1;i<=N;i++)
			scanf("%lf%lf",&X_w[i],&Y_w[i]);
		for(int i=1;i<=N;i++)
			scanf("%lf%lf",&X_b[i],&Y_b[i]);
		for(int i=1;i<=N;i++)
		for(int j=1;j<=N;j++)
			w[j][i]=-sqrt((X_w[i]-X_b[j])*(X_w[i]-X_b[j])+(Y_w[i]-Y_b[j])*(Y_w[i]-Y_b[j]));
		KM();
		for(int i=1;i<=N;i++)printf("%d
",match[i]);
	}
	return 0;
} 
原文地址:https://www.cnblogs.com/martian148/p/13917487.html