题解 UVA1411 【Ants】

题目链接

蒟蒻来写一篇KM算法的题解。

首先,如果所有线段不相交,那么线段长度之和一定最小。证明很简单,在这里不再赘述。(提示:三角形两边之和大于第三边)

那么这道题就转化为了一道带权二分图匹配问题,可以使用费用流求解。又由于黑白点数相同,所以带权最大匹配一定是完备匹配,故可以用KM算法求解。

若不会KM算法,请到这里学习。

然后就把KM算法的板子改一下,在每个黑点与白点之间连一条权值为距离的边,就可以A了。

代码:

#include<bits/stdc++.h>
using namespace std;

const double INF=1e9;

double Map[101][101],la[101],lb[101],va[101],vb[101],upd[101],delta;//一定要开double
int match[101],n;

bool dfs(int x){
	va[x]=1;
	for(int i=1;i<=n;i++){
		if(!vb[i]){
			if(fabs(la[x]+lb[i]-Map[x][i])<=0.0001){//注意精度
				vb[i]=1;
				if(!match[i]||dfs(match[i])){
					match[i]=x;
					return true;
				}
			}
			else{
				upd[i]=min(upd[i],la[x]+lb[i]-Map[x][i]);
			} 
		}
	}
	return false;
}

void KM(){//KM算法,不多解释
	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],Map[i][j]);
		}
	}
	
	for(int i=1;i<=n;i++){
		while(1){
			memset(va,0,sizeof(va));
			memset(vb,0,sizeof(vb));
			for(int i=1;i<=n;i++){
				upd[i]=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;
			}
		}
	}
}

void init(){
	memset(Map,0,sizeof(Map));
	memset(match,0,sizeof(match));
}

int main() {
	double x1[101],x2[101],y1[101],y2[101];
	while(scanf("%d",&n)!=EOF){
		init();
		
		for(int i=1;i<=n;i++){
			scanf("%lf %lf",&x2[i],&y2[i]);
		}
		for(int i=1;i<=n;i++){
			scanf("%lf %lf",&x1[i],&y1[i]);
		}
		
		for(int i=1;i<=n;++i){
			for(int j=1;j<=n;++j){
				Map[i][j]=-double(sqrt((x1[i]-x2[j])*(x1[i]-x2[j])+(y1[i]-y2[j])*(y1[i]-y2[j])));
			}
		}//计算所有黑白点之间的距离,并连边
		KM();//KM求带权匹配
		for(int i=1;i<=n;i++){
			printf("%d\n",match[i]);
		}//输出结果
	}
    return 0;
}

$ \Large\texttt{My}\texttt{Blog}$

posted @ 2019-09-14 00:33  _逃离地球  阅读(145)  评论(1编辑  收藏  举报