HDU 4313 Matrix (贪心+并查集)

来源:互联网 发布:解封淘宝店铺要多少钱 编辑:程序博客网 时间:2024/04/28 03:38

题意:给你一个有n(2<=n<=100000)个节点的树,树中每条边都有一个权值。然后再给你k(2<=k<=n)个点,表示这些点上有一个机器人。最后让你删去一些边使任意两个机器人都不能互达,且所删边的权值之和要最小。

思路:我最开始想到的是:

1、将边按权值由小到大排序。

2、计算每条边连接的两个子树中分别有多少个机器人。

3、然后,枚举每条边,如果该条边所连接的两个子树中都有机器人,则将该条边删除。

4、重复步骤2和步骤3,直到枚举完所有的边。

5、所删除的边的权值之和就是要求的结果。

但是,这样做时间复杂度太高,主要是第2步花了太多的时间。后来,发现,完全可以反过来做,思路如下:

1、初始化每个节点为一个集合,并记录每个集合中机器人的数目。

2、将边按权值由大到小排序。

3、枚举每条边,如果该边两端点所在的集合最多只有一个机器人,则合并这两个集合(用并查集),这条边不用删除。否则,这条边要删除。

4、重复步骤3直到枚举完所有边。

5、所删除的边的权值之和就是要求的结果。


代码如下:

#include <iostream>#include <cstring>#include <cstdio>#include <algorithm>#include <cmath>#include <vector>using namespace std;const int maxn = 100010;int t, n, k;int mach[maxn], father[maxn];struct Edge {int u, v, c;}edge;vector<Edge> vv;bool cmp(Edge a, Edge b){return a.c > b.c;}void initSet(){for (int i = 0; i < maxn; ++i) {father[i] = i;mach[i] = 0;}}int find(int x){if (x != father[x]) {father[x] = find(father[x]);}return father[x];}void merge(int a, int b){a = find(a);b = find(b);if (a == b) return ;if (a < b) {father[b] = a;mach[a] += mach[b];} else {father[a] = b;mach[b] += mach[a];}}int main(){scanf("%d", &t);while (t--) {scanf("%d%d", &n, &k);vv.clear();initSet();int u, v, c;__int64 ans = 0;for (int i = 0; i < n - 1; ++i) {scanf("%d%d%d", &u, &v, &c);edge.u = u; edge.v = v; edge.c = c;vv.push_back(edge);ans += c;}for (int i = 0; i < k; ++i) {scanf("%d", &u);mach[u] = 1;}sort(vv.begin(), vv.end(), cmp);for (int i = 0; i < n - 1; ++i) {u = find(vv[i].u);v = find(vv[i].v);if (mach[u] + mach[v] <= 1) {merge(u, v);ans -= vv[i].c;}}printf("%I64d\n", ans);}return 0;}