并查集
并查集用来判断许多元素是否相关,有初始化、寻找父节点、链接三种操作,可以用来做最小生成树的问题
博主持续更新中
并查集基本操作
洛谷 P3367 【模板】并查集
#include<bits/stdc++.h>
using namespace std;
#define foo(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
const int N=1e4+10;
int Fa[N],a[N];
void init(int x)//初始化
{
Fa[x]=x;
return;
}
int find(int x)//寻找父节点
{
if(Fa[x]!=x)
{
Fa[x]=find(Fa[x]);
}
return Fa[x];//这里为了Fa[x]直接指向父节点上,方便后面的查找
}
void connect(int x,int y)//链接
{
//x,y are ancestors
Fa[x]=y;
return;
}
int main()
{
cin.tie(0);
int n,m;
cin>>n>>m;
foo(i,1,n)
{
init(i);
}
while(m--)
{
int x,y,z;
cin>>x>>y>>z;
if(x==1)
{
connect(find(y),find(z));
}
else
{
if(find(y)==find(z))
{
cout<<"Y\n";
}
else cout<<"N\n";
}
}
return 0;
}
最小生成树
Kruskal算法(加边法)
洛谷 P2820 局域网
#include<bits/stdc++.h>
using namespace std;
#define foo(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
const int N=200;
struct edge
{
int start1,end1,con_sit;
}Edge[20000];
int father[N];
int connect=0;
void init(int x)
{
father[x]=x;
}
bool compare(edge a,edge b)
{
return a.con_sit<b.con_sit;
}//结构体里面元素的compare
int find(int x)
{
if(father[x]!=x)
{
father[x]=find(father[x]);
}
return father[x];
}
void connect_(int x,int y)
{
father[find(x)]=find(y);
}
int main()
{
int n,k,ans=0,use=0;
cin>>n>>k;
foo(i,1,n)init(i);
foo(i,1,k)
{
cin>>Edge[i].start1>>Edge[i].end1>>Edge[i].con_sit;
ans+=Edge[i].con_sit;
}
sort(Edge+1,Edge+k+1,compare);//把所有边从小到大排序
for(int i=1;i<=k&&connect<n-1;i++)//直到边全用完或者连接了n-1条边
{
if(find(Edge[i].start1)!=find(Edge[i].end1))//并查集判断是否在同一个树上,如果不是则连接
{
connect_(Edge[i].start1,Edge[i].end1);
connect++;
use+=Edge[i].con_sit;
}
}
if(connect==n-1)cout<<ans-use;
else cout<<"-1";
return 0;
}
Prim算法(加点法)
洛谷 P1111 修复公路
#include<bits/stdc++.h>
using namespace std;
#define foo(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
const int N=1e3+10;
int tree[N][N],connect_mintime[N],INF=2e5+10,ans=0;
bool join[N];
int prim(int n)
{
//建立最小生成树
join[1]=true;
connect_mintime[1]=0;
foo(i,2,n)
{
connect_mintime[i]=min(connect_mintime[i],tree[1][i]);//从某一个顶点开始,找最短链接,算法迭代到每个点,先记录第一个点与其他所有点的最短路径
}
foo(i,2,n)
{
int temp=INF;
int tidx=-1;
foo(j,2,n)
{
if(!join[j]&&connect_mintime[j]<temp)//找到还没加入的最短的路径
{
temp=connect_mintime[j];
tidx=j;
}
}
if(tidx==-1)//没有路径
{
return -1;
}
join[tidx]=true;
ans=max(ans,connect_mintime[tidx]);
foo(j,2,n)connect_mintime[j]=min(connect_mintime[j],tree[tidx][j]);//更新每个点到已生成树的最短路径
}
}
int main()
{
int n,m;
cin>>n>>m;
foo(i,1,n)//n
{
foo(j,1,n)
{
tree[i][j]=INF;
}
}
memset(connect_mintime,INF,sizeof(int)*(n+1));//注意这里是n+1
while(m--)
{
int a,b,t;
cin>>a>>b>>t;
tree[a][b]=tree[b][a]=t;//用数组表示两边的对应关系
}
if(prim(n)==-1)cout<<"-1";
else cout<<ans;
return 0;
}
Tarjan
在合并已经遍历过的对象时需要利用并查集