Aizu 2224(并查集)

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;
}
View Code
原文地址:https://www.cnblogs.com/daydayupacm/p/5724950.html