HOJ 3938 Portal (最小生成树,kruskal,并查集)

62 篇文章 0 订阅

最小生成树,kruskal,并查集
题目意思:
n个点,m条边的图,双向边。问,权值是 len 的时候,有多少对点 x 和 y,可以从x 走到 y;
当权值 大于等于某条边的长度的时候,才能走那条边。 从点x 走到 y, 和从y 走到 x, 算是一个 点对。

本题要点:
1、实际模拟了 kruskal算法的过程。 每次选择一条当前最短的边(假设两点x, y, 边长 z),加入最小生成树,当点的两点 x 和 y
不属于同一个并查集的时候,这条边才被加入。 并且,如果 权值len == z 的时候,增加的可以走的点对的数量是
num[fay] * num[fax], fay, fax 分别是 点y和点x 所在并查集的父节点。
2、 用一个数组 merge_edge 来存放加入最小生成树的所有边。 数组merge_edge_num 则放对应的那条边可以走的点的对数。
3、 本题的查询量很大,需要处理好数组 merge_edge 和 merge_edge_num, 然后调用 stl 的 upper_bound 函数,
在 数组 merge_edge 查找第一个大于 当前查询的权值len的下标id, id - 1 表示 最后一个小于权值len的下标。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MaxN = 10010, MaxM = 100010;
int fa[MaxN], num[MaxN];
int n, m, query;
int merge_edge[MaxN], cnt;	// merge_edge[k] 表示加入最小生成树的第k条边
int merge_edge_num[MaxN];	//merge_edge_num[k] 表示边长不超过最小生成树的第k条边,可以走的点的对数

struct rec
{
	int x, y, z;
	bool operator<(const rec& rhs) const
	{
		return z < rhs.z;
	}
}edge[MaxM];

int get(int x)
{
	if(x == fa[x])
		return x;
	return fa[x] = get(fa[x]);
}

void solve()
{
	cnt = 0;
	sort(edge + 1, edge + m + 1);	
	for(int i = 1; i <= n; ++i)
	{
		fa[i] = i, num[i] = 1;
	}
	int sum = 0;
	for(int i = 1; i <= m; ++i)
	{
		int x = get(edge[i].x), y = get(edge[i].y);
		if(x == y)	continue;
		fa[x] = y;
		sum += num[y] * num[x];
		num[y] += num[x];
		merge_edge[++cnt] = edge[i].z;
		merge_edge_num[cnt] = sum;		
	}
	int len;
	for(int i = 0; i < query; ++i)
	{
		scanf("%d", &len);
		int id = upper_bound(merge_edge + 1, merge_edge + cnt + 1, len) - merge_edge;
		--id;
		printf("%d\n", merge_edge_num[id]);
	}
}

int main()
{
	while(scanf("%d%d%d", &n, &m, &query) != EOF)
	{
		for(int i = 1; i <= m; ++i)
		{
			scanf("%d%d%d", &edge[i].x, &edge[i].y, &edge[i].z);	
		}
		solve();
	}
	return 0;
}

/*
10 10 10
7 2 1
6 8 3
4 5 8
5 8 2
2 8 9
6 4 5
2 1 5
8 10 5
7 3 7
7 8 8
10
6
1
5
9
1
8
2
7
6
*/

/*
36
13
1
13
36
1
36
2
16
13
*/
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值