算法:并查集

3 阅读3分钟

并查集

1.初始化

	for(int i =1;i<=n;i++)
      fa[i]=i;

对于1到n个元素,用一个数组fa[]来储存每一个元素的父节点。 一开始,我们先将父节点设置为自己

2.查询

找到i的祖先并直接返回,未进行路径压缩

int find(int x)
{	
 	return fa[x] == x ? x: find(fa[x]);
}

路径压缩版//节省时间

int find(int x)
{
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}

3.合并

void merge(int x,int y)
{
 	int fx = find(x);//找到x的祖先
  	int fy = find(y); //找到y的祖先
  	if(fx == fy) return ;//如果是同一个祖先,直接跳过
  	fa[fx] = fy;//x的祖先指向y的祖先
}

4.查询

bool query(int x,int y)
{
 	int fx = find[x] ,fy = find(y);
  	if(fx == fy) return true;//如果他们的祖先是同一个,说明他们是一个家族的
  	else return false;
}

例题:P1551亲戚 题解:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 5e3+5;
ll n,m,p;
ll m1,m2;
ll p1,p2;
ll fa[N];
ll find(ll x)
{
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void merge(ll x,ll y)
{
	ll fx = find(x), fy = find(y);
	if(fx == fy) return ;
	fa[fx]=fy;
}
ll query(ll x,ll y)
{
	ll fx =find(x),fy = find(y);
	if(fx == fy) return 1;
	return 0;
}
int main()
{
	cin>>n>>m>>p;
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		cin>>m1>>m2;
		merge(m1,m2);
	}
	for(int i=1;i<=p;i++)
	{
		cin>>p1>>p2;
		if(query(p1,p2))
		cout<<"Yes"<<"\n";
		else
		cout<<"No"<<"\n";
	}
	return 0;
}

例题:字符串并查集P2256——中校运会之百米跑

map大法!!! 题解:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e4+5;
ll n,m,k;

map<string,string> fa;
string find(string s)
{
	return fa[s] == s? s:fa[s] = find(fa[s]) ;
}
void merge(string s1,string s2)
{
	string fx = find(s1),fy = find(s2);
	if(fx == fy) return ;
	fa[fx] = fy;
}
bool query(string s1,string s2)
{
	string fx = find(s1),fy = find(s2);
	if(fx == fy) return true;
	else return false;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		string s;
		cin>>s;
		fa[s]=s;
	}
	while(m--)
	{
		string s1;
		string s2;
		cin>>s1>>s2;
		merge(s1,s2);
	}
	cin>>k;
	while(k--)
	{
		string s1;
		string s2;
		cin>>s1>>s2;
		if(query(s1,s2)) cout<<"Yes."<<"\n";
		else cout<<"No."<<"\n";
	}
	return 0;
}

例题:P1111修复公路

题解:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e5+5;
ll n,m;
ll x[N],y[N],t[N];
ll fa[N];
struct node 
{
	ll x;
	ll y;
	ll t;
}cv[N];
ll cmd(node sx, node sy)
{
	return sx.t<sy.t;
}
ll find(ll x)
{
	return fa[x] == x ? x : fa[x] = find(fa[x]); 
}
void merge(ll x,ll y)
{
	ll fx = find(x) , fy = find(y);
	if(fx == fy) return ;
	fa[fx] = fy;
}
bool check()
{
	ll sum=0;
	for(int i=1;i<=n;i++)
	{
		if(fa[i]==i) sum++;// 找最大的老大,找到后帮派加一 
		if(sum==2) return 0;//如果出现第二个帮派,就说明路并没有连同在一块,所以直接返回 
	}
	return 1;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		cin>>cv[i].x>>cv[i].y>>cv[i].t;
	}
	sort(cv+1,cv+1+m,cmd);
	for(int i=1;i<=m;i++)
	{
		merge(cv[i].x,cv[i].y);
		if(check())
		{
			cout<<cv[i].t;
			return 0;
		}
	}
	cout<<-1<<"\n";
}

例题:P8654 [蓝桥杯 2017 国 C] 合根植物(水题)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e6+5;
ll m,n,k;
ll fa[N];
ll find(ll x)
{
	return fa[x] == x ? x : fa[x] = find(fa[x]); 
}
void merge(ll x,ll y)
{
	ll fx = find(x), fy = find(y);
	if(fx == fy) return ;
	fa[fx] = fy;
}
ll res;
int main()
{
	cin>>m>>n>>k;
	ll ans= m*n;
	for(int i=1;i<=ans;i++)
	{
		fa[i]=i;
	}
	while(k--)
	{
		ll a,b;
		cin>>a>>b;
		merge(a,b);
	}
	for(int i=1;i<=ans;i++)
	{
		if(fa[i]==i) 
		{
			res++;
		}
	}
	cout<<res;
	return 0;
	
}