http://acm.hust.edu.cn/vjudge/contest/125004#problem/B
题意:一个邪恶的女巫因为嫉妒Nicholas家的猫可爱,就施了魔法让他家的猫咪困在卷子里出不来了(要不然说她邪恶呢)。现在给出她施魔法变出来的桩的坐标,栅栏i与栅栏j(是一个整体,也就是给出了一条边,边的长度是第i个桩与第j个桩间的长度)。现在让你求最少需要破坏多长的栅栏才能让可爱的猫咪都出来。
分析:若想要让猫咪出来的话,肯定是不能有环了。那么现在就有一个思想:算出全部边长总和,然后减去可以构成的最大生成树的总和(不能有环,生成树正好符合这样的条件),那么剩下的就是需要被破坏的边的总和。但是怎样求最大生成树呢?我们需要用到并查集,首先将边存在结构体里,然后按照从大到小将边长排序,接着需要遍历m条边,当加入某条边时,若两端的father[x]不同,则需要进行归并(father[x]=y),若相同的话,自然不需要加它这条边的边长了(相同的话,会构成一个封闭区间)。
注意:没有说m的范围,在开有关m的数组时,需要比n大。
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> #include<math.h> #define maxn 11000 #define oo 0xfffffff using namespace std; int father[maxn], n, m; struct node { int u, v; double w; } s[maxn*100];///数组大小 struct point { int x, y; } a[maxn]; double Len(int p, int q) { double d = sqrt((a[p].x-a[q].x)*(a[p].x-a[q].x)+(a[p].y-a[q].y)*(a[p].y-a[q].y)); return d; } int Find(int x) { while(x!=father[x]) { x=father[x]; } return x; } int cmp(node p, node q) { return p.w>q.w; } double solve() { double ans = 0; for(int i=0; i<m; i++) { int ru = Find(s[i].u); int rv = Find(s[i].v); if(ru != rv) { father[ru] = rv; ans += s[i].w; } } return ans; } int main() { int u, v; while(scanf("%d %d",&n, &m)!=EOF) { double sum = 0; for(int i=1; i<=n; i++) scanf("%d %d", &a[i].x, &a[i].y); for(int i=1; i<=n; i++) father[i] = i; for(int i=0; i<m; i++) { scanf("%d %d", &u, &v); s[i].u = u; s[i].v = v; s[i].w = Len(u, v); sum += s[i].w; } sort(s, s+m, cmp); double l = solve(); printf("%.3f ", sum-l); } return 0; }