poj 3723 最大权森林 最小生成树的运用

http://poj.org/problem?id=3723

之前看过但是不理解,今天看了一下感觉是恍然大悟的感觉。

对于每个人之间的关系可以看做是一条边,亲密度可以看做是这条边的权值。既然我们要招募所有人(联想到最小生成树)使这个费用最小,那么亲密度应该尽量选择大的,这里可以巧妙的处理,将权值都取负,正数的最大不就是负数的最小吗?

但这里要注意到这不一定是一棵树,可能是森林,题目中没有保证说所有人之间都是连通的。

不过我们还是可以使用最小生成树来做。找出可以经过所有人的最小的边。

WA了好几发,因为合并集合的时候写错了,应该将这条边一个点的祖先添加到另一个点的祖先下面,而不是将一个点添加到另一个点下面。。。。

太久没写都生疏了。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 100009
typedef struct edge
{
    int from;
    int to;
    int cost;
}edge;
int n,m,r;
int p[M];
edge e[M];
int ans;
int cmp(edge a,edge b)
{
    return a.cost < b.cost;
}
int find(int x)
{
    return x==p[x]? x: p[x]=find(p[x]);
}
int kruskal()
{
    for(int i = 0;i < n+m;i++)
        p[i] = i;
    for(int i = 0;i < r;i++)
    {
        /*int x = e[i].from;
        int y = e[i].to;
        if(find(x)!=find(y))
        {
            p[x] = y;  //找了好久的错。。。要写成p[find(x)] = y;
            ans += e[i].cost;
        }*/
        int x = find(e[i].from);
        int y = find(e[i].to);
        if(x!=y)
        {
            p[x] = y;  //一定要记得集合合并的时候是将一个的祖先已到另一个祖先的下面,从而达到两个集合的合并
            ans += e[i].cost;
        }
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        //memset(e,0,sizeof(e));
        ans = 0;
        scanf("%d %d %d",&n,&m,&r);
        for(int i = 0;i < r;i++)
        {
            int a,b,c;
            scanf("%d %d %d",&a,&b,&c);
            e[i] = (edge){a,b+n,-c};
        }
        sort(e,e+r,cmp);
        printf("%d\n",10000*(n+m)+kruskal());
    }
    return 0;
}


你可能感兴趣的:(poj 3723 最大权森林 最小生成树的运用)