Java教程

Boruvka 算法

本文主要是介绍Boruvka 算法,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Boruvka算法解决某些问题超级好用。

这些问题形如,给你n个点,每个点有点权,任意两个点之间有边权,边权为两个点权用过某种计算方式得出。

求最小生成树。

通常用 \(O(log n)\) 的时间可以找到与点i连边的边权最小的j

我们考虑这样一个求最小生成树的算法:

考虑维护当前的连通块(初始每个点为独立的一个连通块)

对每个连通块,找到一条与该连通块相连的,且另一端点不在此连通块中的边权最小的边。

将所有的这些边都加入最小生成树,注意,当加入一条边时需判断该边的两端点是否在同一连通块。

重复若干遍上述操作,直到图连通。

这个算法叫Boruvka算法。

复杂度分析:每次连通块个数至少减半,则复杂度为 \(O((n+m)logn)\) ,还有个并查集假装是 \(O(1)\)

但是此算法通常不用于求裸的最小生成树(Kruskal多好用)

可是在解决上述问题时,往往有奇效。

我们发现,我们只需要求出与每个连通块相连的边权最小的边即可,在这种类型的题目中,这个东西复杂度一般为 \(O(n log n)\)

所以我们就可以在 \(O(n log^2 n)\) 的复杂度下解决此类问题。

CF888G Xor-MST

给定 \(n\) 个结点的无向完全图。每个点有一个点权为 \(a_i\)。连接 \(i\) 号结点和 \(j\) 号结点的边的边权为 \(a_i\oplus a_j\)。
求这个图的多生成树的权值。
\(1\le n\le 2\times 10^5\),\(0\le a_i< 2^{30}\)。

/*header*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#define rep(i , x, p) for(int i = x;i <= p;++ i)
#define sep(i , x, p) for(int i = x;i >= p;-- i)
#define gc getchar()
#define pc putchar
#define ll long long
#define mk make_pair
#define fi first
#define se second
using std::min;
using std::max;
using std::swap;
const int inf = 0x3f3f3f3f;
const int maxN = 5000 + 7;
const int maxM = 200000 + 7;

inline int gi() {
    int x = 0,f = 1;char c = gc;
    while(c < '0' || c > '9') {if(c == '-')f = -1;c = gc;}
    while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = gc;}return x * f;
}

struct Node {
	int u , v, w;
}Map[maxM]; 
int n , m;
int f[maxN];
bool vis[maxM];//该边是否用过 
int d[maxN] , a[maxN];//各子树的最小连外边的权值
int id[maxN];// 记录各子树的最小连外边的编号 

int find(int x) {return f[x] == x ? x : f[x] = find(f[x]);}
int unqie(int x , int y) {f[find(x)] = find(y);}

int Boruvka() {	
	for(int i = 1;i <= n;++ i) f[i] = i;
	int sum = 0;
	while(true) {
		bool flag = false;
		for(int i = 1;i <= n;++ i) d[i] = inf;
		for(int i = 1;i <= m;++ i) {
			int u = find(Map[i].u) , v = find(Map[i].v), c = a[u] & a[v];
			if(u == v) continue;
			flag = true;
			if(c < d[u] || (d[u] == c && id[u] > i)) id[u] = i , d[u] = c;
			if(c < d[v] || (d[v] == c && id[v] > i)) id[v] = i , d[v] = c;
		}
		if(!flag) break;
		for(int i = 1;i <= n;++ i) {
			if(d[i] != inf && !vis[id[i]])  {
				unqie(Map[id[i]].u , Map[id[i]].v);
				sum += Map[id[i]].w;
				vis[id[i]] = true;
			}
		}
	}
	int tot = 0;
	for(int i = 1;i <= n;++ i) if(f[i] == i) tot ++;
	tot > 1 ? printf("orz") : printf("%d",sum);
}

int main() {
	n = gi();m = gi();
	for(int i = 1;i <= m;++ i) {
		int u = gi() , v = gi(), w = gi();
		Map[i] = (Node) {u , v, w};
	}
	Boruvka();
	return 0;
}

这篇关于Boruvka 算法的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!