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

15 篇文章 0 订阅
题目链接
题目分析:

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<cstdio >
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
#include<set>
#include<cstring>

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<edge> 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<edge> 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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值