POJ 3723 Conscription 最大生成树 + 并查集

看了挑战上的这道题,一开始根本没反应过来这就是最大生成树。不过仔细想想,如果我们把是朋友或者间接是朋友的人都放在一个集合,那么但我们通过a来招募b的时候,就在a和b中间连一条边,权值就是a和b的亲密度。当我们把这个集合中的人都招募完的时候,是不是集合中的人都被一条条边连接起来了呢?此外,为了使我们的总花费最小,我们肯定希望他们的亲密度之和最大(节省的钱最多),也就是使得这个连通块的边权值最大。这不就是最大生成树吗?(肯定有一个人是花费1w来招募的,然后通过他来招募这个集合的其他人)

如果还想不明白的话,换个角度。肯定有一个人是要花费1w来招募的,我们先招募他,然后在和他认识的人中间找到一个亲密度最大的来招募,然后依次类推,每次都往集合里面插入一个人。这是不是类似prim算法呢?所以从这个角度我们也推出了这是最大生成树问题。

还有一点要注意,因为男女士兵的标号都是0—n - 1.但是实际上他们是不一样的,如果输入的数据是0,0,5000.这就会出现错误。所以我们需要将他们都给区分开。起初我用的是map,一一映射,最后超时了。看了网上大神的代码才发现自己是这么蠢。只需要将女兵的编号加上N(男兵的总数)就行了。

代码如下;

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define lson l, m, k << 1
#define rson m, r, k << 1 | 1

using namespace std;

typedef long long int LL;
const int INF = 0x3f3f3f3f;
const int maxn = 100005;
int p[maxn];
int N, M, R;

int find_root(int x){//并查集查找根节点
    if (p[x] == x) return x;
    return p[x] = find_root(p[x]);
}

struct edge{
    int u, v, dis;
    edge(int a, int b, int c){u = a; v = b; dis = c;}
    edge(){}
    bool operator < (const edge &b) const{
        return dis < b.dis;
    }
};
edge ed[maxn];

int kruskal(){
    for (int i = 0; i < maxn; i++)//并查集初始化
            p[i] = i;

    sort(ed, ed + R);
    int res = 0;
    for (int i = 0; i < R; i++){
        edge e = ed[i];
        int rx = find_root(e.u);
        int ry = find_root(e.v);
        if (rx != ry){
            p[rx] = ry;
            res += e.dis;
        }
    }
    return res;
}

int main()
{
    //freopen("1.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    while (T--){
        scanf("%d%d%d", &N, &M, &R);
        int x, y, d, cnt = 0;
        for (int i = 0; i < R; i++){
            scanf("%d%d%d", &x, &y, &d);
            ed[i] = edge(x, y + N, -d);
        }

        printf("%d\n", 10000 * (M + N) + kruskal());
    }
    return 0;
}


 

 

你可能感兴趣的:(ACM-最小生成树,ACM-并查集)