【ACM】- HDU-4313 Matrix 【最小生成树】

题目链接
题目分析:

N个结点N-1条边连接本身是一棵生成树;需要切断指定的几个结点之间的两两联系,使切断边权值之和最小;
节点编号0 ~ N-1
数据比较大,必须用long long 型存储累加结果,否则WA

解题思路:

【参考解题报告】
需切断K个指定结点之间的联系,即分成K个独立的连通块;
kruskal算法,不过长边优先选择(priority_queue实现大顶堆);
每次判断条件:当两个machine不再在同一集合时,及早连接,但是如果两个machine在同一集合,则此边必须删去()
如果选择小顶堆,两个machine之间的联系可能拖到最后才解决


AC程序(C++)
/**********************************
*@ID: 3stone
*@ACM: HDU-4313 Matrix
*@Time: 18/9/14
*@IDE: VSCode + clang++
***********************************/
#include
#include
#include
#include
#include
#include
#include

using namespace std;
const int maxn = 100010;

//边结构体
struct edge{
    int u, v;
    int cost;
    edge() {}
    edge(int _u, int _v, int _cost) : u(_u), v(_v), cost(_cost) {} //构造函数,便于加入结点
    bool operator < (const edge& n) const { //规定优先级 - 浮点数比较
        return cost < n.cost; //(注意和sort函数是相反的)使形成大顶堆
    }
};
int N, K; //结点数, 特殊结点数
int far[maxn]; //并查集

set<int> machine; //记录破幻者(机器)想利用set.count()功能

//寻根
int find_root(int a) {
    int root = a;
    while(root != far[root]) root = far[root];

    while(a != far[a]) { //路径压缩
        int cur = a;
        a = far[a];
        far[cur] = root;
    }
    return root;
}

//Kruskal算法
long long kruskal(priority_queue E) {
    long long ans = 0;//权值和
    int cnt = 0; //已选择的边数

    while(!E.empty()) {
        edge e = E.top(); E.pop(); //get fisrt edge
        int root_u = find_root(e.u);
        int root_v = find_root(e.v);
        //为什么不用再判断是否属于同一集合了?因为本身连在
        //N-1条边就是生成树啊?
        if(machine.count(root_u) != 0 && machine.count(root_v) != 0) { //两个含有machine的集合
            ans += e.cost;
            //printf("edge cutted: %d %d %d\n", e.u, e.v, e.cost);
            if(++cnt == K - 1) break; //目前已找的machine数量 为 K - 1
        } else { //至少有一个没有machine
            if(machine.count(root_u) != 0) {
                far[root_v] = root_u; //以machine所在点为根
            } else {
                far[root_u] = root_v;
            }
        }

    }//for - i

    return ans;
}//kruskal


int main() {
    int T, u, v, cost;
    scanf("%d", &T); //T组数据
    while(T-- > 0) {
        scanf("%d %d", &N, &K);
        for(int i = 0; i <= N; i++) far[i] = i; //初始化并查集
        machine.clear(); //清空
        priority_queue E; //保存所有边(无clear()函数,每次重新定义时间最快)
                                //注:优先级设置和sort()函数是相反的
        for(int i = 0; i < N - 1; i++) {
            scanf("%d %d %d", &u, &v, &cost); //输入结点坐标
            E.push(edge(u, v, cost)); //加入边集合
        }

        for(int i = 0; i < K; i++) { //input the location of K machines
            scanf("%d", &u); //machine所在的城市编号
            machine.insert(u);
        }

        long long ans = kruskal(E);
        printf("%lld\n", ans); //输出最小成本

    }//while

    system("pause");
    return 0;
}

你可能感兴趣的:(ACM-图,HDU)