理论第十二课——并查集

目录

并查集:

一、什么是并查集?

二、并查集的操作…… 

三、实战!!!

第一题:亲戚

答案一:

第二题:3.1.1最短网络

答案二:

第三题:矿井供电(电网修建)

答案三:

四、结语……


并查集:

一、什么是并查集?

        并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。

二、并查集的操作…… 

        初始化

        把每个点所在集合初始化为其自身

        通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间              复杂度均为O(N)

        查找

        查找元素所在的集合,即根节点

        合并

        将两个元素所在的集合合并为一个集合

        通常来说,合并之前,应先判断两个元素是否属于同一集合​,这可用上面的"查找"操作实现

三、实战!!!

第一题:亲戚

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。
规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。

输入格式:

第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。
以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Ai和Bi具有亲戚关系。
接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。
输出格式:

P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。
样例 1 :

输入:
6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6
输出:
Yes
Yes
No

答案一:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int f[10000];
int find(int n){
    if(f[n]!=n) find(f[n]);
    else return n;}
void together(int n,int m){
    f[find(n)]=find(m);}
int main(){
    int n,m,p,i,x,y;
    cin>>n>>m>>p;
    for(i=1;i<10000;i++) f[i]=i;
    for(i=0;i<m;i++){
        cin>>x>>y;
        together(x,y);}
    for(i=0;i<p;i++){
        cin>>x>>y;
        if(find(x)==find(y)) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;}
    return 0;}

第二题:3.1.1最短网络

农夫约翰被选为他们镇的镇长!
他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场。
约翰已经给他的农场安排了一条高速的网络线路,他想把这条线路共享给其他农场。
约翰的农场的编号是1,其他农场的编号是 2∼n。为了使花费最少,他希望用于连接所有的农场的光纤总长度尽可能短。
你将得到一份各农场之间连接距离的列表,你必须找出能连接所有农场并使所用光纤最短的方案。

输入格式:

第一行包含一个整数 n,表示农场个数。
接下来 n 行,每行包含 n 个整数,输入一个对角线上全是0的对称矩阵。
其中第 x+1 行 y 列的整数表示连接农场 x 和农场 y 所需要的光纤长度。
输出格式:

输出一个整数,表示所需的最小光纤长度。
限制:

3≤n≤100
每两个农场间的距离均是非负整数且不超过100000。
样例 1 :

输入:
4
0  4  9  21
4  0  8  17
9  8  0  16
21 17 16  0
输出:
28

答案二:

#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int m,n,sum,e[N][N],vis[N],dis[N];
int inf = 99999999;
void Prim() 
{
    int min,k;
    for(int i = 1; i <= n; i ++) 
	{
        dis[i] = e[1][i];
        vis[i] = 0;
    }
    dis[1] = 0;
    vis[1] = 1;
    for(int i = 1; i < n; i ++) 
	{
        min = inf;
        for(int j = 1; j <= n; j ++)
            if(vis[j] == 0 && dis[j] < min)
            {
                min = dis[j];
                k = j;
            }
        sum += min;
        vis[k] = 1;
        for(int j = 1; j <= n; j ++)
            if(vis[j] == 0 && dis[j] > e[k][j])
                dis[j] = e[k][j];
    }
}
int main()  
{
    int i,j;
    scanf("%d",&n); 
    sum = 0;
    for(i = 1; i <= n; i ++)
        for(j = 1; j <= n; j ++)
            scanf("%d",&e[i][j]);
    Prim();
    printf("%d\n",sum);
    return 0;
}

第三题:矿井供电(电网修建)

发展采矿业当然首先得有矿井,小明花了上次探险获得的千分之一的财富请人在岛上挖了n口矿井,但他似乎忘记考虑的矿井供电问题……为了保证电力的供应,小明想到了两种办法:
1、在这一口矿井上建立一个发电站,费用为v(发电站的输出功率可以供给任意多个矿井)。
2、将这口矿井与另外的已经有电力供应的矿井之间建立电网,费用为p。
请问需要保证所有矿井电力供应的最小花费是多少。

输入格式:

第1行:一个整数 n,表示矿井总数。 
    第2~n+1行:每行一个整数,第i个数v[i]表示在第i 口矿井上建立发电站的费用。  
    接下来为一个 n*n 的矩阵 P,其中p[ i , j ]表示在第i口矿井和第 j口矿井之间建立电网的费用(数据保证有 p[ i, j ] = p[ j, i ], 且 p[ i, i ]=0) 。
输出格式:

仅一个整数,表示让所有矿井获得充足电能的最小花费。
提示:

对于30%的数据:1<=n<=50; 
对于100%的数据:1<=n<=300;   0<=v[i], p[i,j] <=100000.
样例 1 :

输入:
4 
5 
4 
4 
3 
0 2 2 2 
2 0 3 3 
2 3 0 4 
2 3 4 0
输出:
9
说明:
小明可以选择在4号矿井建立发电站然后把所有矿井都与其建立电网,总花费是3+2+2+2 = 9。

答案三:

#include<bits/stdc++.h>
using namespace std;
struct node {
	int u,v,w;
}p[100001];
int n,f[10000],k,ans;
int gf(int u){
	if(f[u]==u) return u;
	else return gf(f[u]);
}
bool cmp(node x,node y)
{
	return x.w<y.w;
}
int main()
{
	int t,cnt=0; cin>>n;
	for(int i=1;i<=n;i++){
		cin>>t; k++;
		p[k].u=0;
		p[k].v=i;
		p[k].w=t;
	}
	for(int i=1;i<=n;i++) f[i]=i;
	for(int i=1;i<=n;i++) 
		for(int j=1;j<=n;j++)
		{
			cin>>t;
			if(j>i)
			{
			    k++;
				p[k].u=i;
				p[k].v=j;
				p[k].w=t;
			}
		}
	sort(p+1,p+k+1,cmp);
	for(int i=1;i<=k;i++)
	{
		int fu,fv;
		fu=gf(p[i].u);
		fv=gf(p[i].v);
		if(fu!=fv)
		{
			f[fv]=p[i].u; cnt++;
			ans+=p[i].w;
			if(cnt==n) break;
		}
	}
	cout<<ans;
	return 0;
}

四、结语……

        那么今天就到此为止了,你们学会了么?

  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 18
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值