并查集、最小生成树和tarjan算法

并查集

并查集用来判断许多元素是否相关,有初始化、寻找父节点、链接三种操作,可以用来做最小生成树的问题
博主持续更新中

并查集基本操作

洛谷 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

在合并已经遍历过的对象时需要利用并查集

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值