并查集板子+kruskal
最近在学最小生成树得时候又用到了并查集,一起来整理一下
1.并查集
并查集就是字面意思,将两个单独得集合合并成一个大的集合。
并查集关键在于两个操作:合并和查找
先要完成查找操作(合并操作在查找的基础上)
int find(int x)
{
return root[x] == x ? x : root[x]=find(root[x]);
}
查找x个体所在的根结点,从而确定集合关系 ,find函数缩短路径 ,把一个集合中所有元素都指向一个根结点,减少树的高度 ,如果x的根结点root[x]为自己则返回x,如果不是x的根结点root[x]=root[x]的根节点递归找到那个共同的根。
最后就是合并操作
void add(int x,int y)
{
int n=find(x);
int m=find(y);
if(n!=m) root[n]=m;
}
合并操作先检查两者是否已经在同一集合,如果不在同一集合(不为相同的根节点)则把两元素的根结点相连达到合并目的,两集合合并成同一集合。
2.kruskal板子 最小生成树
kruskal是把图的边视为集合中的元素,用贪心思想,把边的权值从小到大遍历,找到n-1条边连接n个节点,最重要的是 防止集合中的边形成环导致不为最小生成树
来自洛谷 :P3366最小生成树板子
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <cmath>
#include <set>
#include <queue>
#define ri int
typedef int ll;
typedef long long lll;
using namespace std;
const long long mod=1e9+7;
ll n,m;
ll p[200005]; //root数组
struct e{
ll f;
ll e;
ll w; //权值
}a[200005]; //边集合
bool cmp(e a,e b)
{
return a.w<b.w;
}
ll find(ll x) //查
{
return p[x] == x ? x : p[x]=find(p[x]);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin >> n >> m;
for(ri i=1;i<=m;++i)
cin >> a[i].f >> a[i].e >> a[i].w;
for(ri i=1;i<=n;++i) p[i]=i;
sort(a+1,a+1+m,cmp); //按照权值排序从小到大
ll l,r;
ll ans=0;
for(ri i=1;i<=m;++i)
{
l=find(a[i].f);
r=find(a[i].e);
if(l!=r) //合并
{
ans+=a[i].w;
p[l]=r;
}
}
cout << ans << '\n';
return 0;
}