HDU 4313 Matrix (贪心+并查集)

26 篇文章 0 订阅
3 篇文章 0 订阅

题意:给你一个有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;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值