最小生成树----Save your cats+kruskal

https://vjudge.net/problem/Aizu-2224
题意:很多猫被女巫用篱笆围了起来,给出连接每一个栅栏的两个点,问如何破坏栅栏使消耗最小(即边最小)并且解救出所有被围起来的猫

最小生成树----Save your cats+kruskal_第1张图片
直接看样例三,如箭头所示,我们只要解开这两条栅栏,就可以解放所有的猫,因为是被围起来的嘛,所以只需要开一个缺口,被围的猫不就可以被放出来了吗。
那么又如何找到这些最短的边呢,我们可以通过最大生成树来寻找,(为什么不是最小生成树?)因为这样我们就可以得到较小的边。
另外生成树不可以有环,我们的目的就是要破坏原本图中的这些环,最大生成树刚好可以满足没有环的条件,剩下的边不就是构成这些环的最小边了吗。问题解决。
用kruskal求解,先用优先队列进行处理,从大到小排序

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1e5+5;
double xx[maxn],yy[maxn];
int pre[maxn];
struct edge{
	int from,to;
	double cost;
	edge(){};
	edge(int a,int b,double c){
		from=a;to=b;cost=c;
	}
}star[5000005];
bool cmp(edge a,edge b){//排序 
	return a.cost>b.cost;
}
double ans,sum;

int Find(int x){
	if(x!=pre[x])pre[x]=Find(pre[x]);
	return pre[x];
}

void kruskal(int n,int m){
	int num_edge=0;//当前边数
	
	for(int i=1;i<=n;i++)pre[i]=i;//并查集初始化 
	
	
	sort(star+1,star+1+m,cmp);//排序
	
	
	for(int i=1;i<=m;i++){
		//查询两个点是否在同一个集合,如果在则不行 
		int fa=Find(star[i].from);
		int fb=Find(star[i].to);
		if(fa!=fb){//合并 
			pre[fb]=fa;
			
			ans+=star[i].cost;
			num_edge++;
			if(num_edge==n-1) break;
		}
		
	}
	double res=sum-ans;
	printf("%.3lf",res);
	
}
int main( )
{	
	//ios::sync_with_stdio(false);
    //cin.tie(0);
    
    int n,m;
    int from,to,dir,cost;
    while(~scanf("%d%d",&n,&m)){
    	//if(n==0&&m==0)return 0;
    	ans=0;sum=0;
    	//坐标 
    	for(int i=1;i<=n;i++){
    		scanf("%lf%lf",&xx[i],&yy[i]);
    	}
    	//路径 
    	int a,b;
    	for(int i=1;i<=m;i++){
    		scanf("%d%d",&a,&b);
    		//距离 
    		double temp=sqrt((xx[a]-xx[b])*(xx[a]-xx[b])+(yy[a]-yy[b])*(yy[a]-yy[b]));
    		star[i]=edge(a,b,temp);
    		sum+=temp;
		}
    	kruskal(n,m);
    }
    
    return 0;
}

你可能感兴趣的:(最小生成树)