并查集+题目

并查集

并查集是一种维护集合的数据结构,它的名字中“并”“查”“集”分别取自Union(合并),Find(查找),Set(集合)这3个单词。

初始化

一开始每个元素都是独立的一个集合

for(int i=1;i<=N;i++)
{
	fa[i]=i;
}

查找

由于规定同一个集合中只存在一个根结点,因此查找操作就是对给定的结点寻找其根节点的过程

递推
int find(int x)
{
	while(x!=fa[x])
	{
		x=fa[x];
	}
	return x;
}
递归
int find(int x)
{
	if(x==fa[x])
	{
		return x;
	}
	else{
		return find(fa[x]);
	}
}
路径压缩
int find(int x)
{
	if(x==fa[x])
	{
		return x;
	}
	else{
		fa[x]=find(fa[x])
		return fa[x];
	}
}

合并

void unio(int i,int j)
{
	int x1=find(i);
	int x2=find(j);
	fa[x1]=x2;
}

L2-024 部落 (25 分)

题目链接

题意

在一个社区里,每个人都有自己的小圈子,还可能同时属于很多不同的朋友圈。我们认为朋友的朋友都算在一个部落里,于是要请你统计一下,在一个给定社区中,到底有多少个互不相交的部落?并且检查任意两个人是否属于同一个部落。

思路

  1. 初始化,将属于同一部落的进行合并
  2. 用set计算共有多少人
  3. 有多少个互不相交的部落,遍历所有人如果那个人的祖先就是自己,多一个部落
  4. 任意两个人如果祖先相同就是同一部落

坑点

  1. 要先将第一个人存入部落方便后面的合并

算法一:set+并查集

代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int fa[N];
int find(int i)//查找
{
    if(i==fa[i])
    {
        return i;
    }
    else{
        fa[i]=find(fa[i]);
        return fa[i];
    }
}
void unio(int i,int j)//合并
{
    int x1=find(i);
    int x2=find(j);
    fa[x2]=x1;
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<=N;i++)//初始化
    {
        fa[i]=i;
    }
    set<int>st;//set计算共有多少人
    for(int i=0;i<n;i++)
    {
        int k,x,x1;
        cin>>k;
        if(k>0)
        {
            cin>>x;//先将第一个人存入部落方便后面的合并
            st.insert(x);
        }
        for(int j=1;j<k;j++)
        {
            cin>>x1;
            st.insert(x1);
            unio(x,x1);//依次合并为同一祖先
        }
    }
    int ans=0;//计算共有多少不同部落
    for(int i=1;i<=st.size();i++)
    {
        if(fa[i]==i)//如果那个人的祖先就是自己,多一个部落
        {
            ans++;
        }
    }
    cout<<st.size()<<" "<<ans<<endl;
    int f;
    cin>>f;
    while(f--)
    {
        int a,b;
        cin>>a>>b;
        if(find(a)==find(b))//如果两个人的祖先相同
        {
            cout<<"Y"<<endl;
        }
        else{
            cout<<"N"<<endl;
        }
    }
    return 0;
}

总结

并查集模板题

L2-007 家庭房产 (25 分)

题目链接

题意

给定每个人的家庭成员和其自己名下的房产,请你统计出每个家庭的人口数、人均房产面积及房产套数。

思路

  1. 完成并查集的初始化,查找,合并
  2. 利用3个结构体分别存放初始输入家庭信息,统计家庭房产,面积信息,合并后最后输出家庭信息

坑点

  1. 注意排序的规律
  2. 判断那个人是否还活着(是否不为-1)
  3. 最后的输出不重复

算法一:并查集+结构体

代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct name {
    int id;//编号
    int sum;//家庭人口数
    double fct;//房产套数
    double area;//面积
}a[N];
int fa[N];//祖先
int st[N]={0};//判断编号是否存在
bool cmp(name a,name b)//家庭信息首先按人均面积降序输出,若有并列,则按成员编号的升序输出。
{
    if(a.area==b.area)
    {
        return a.id<b.id;
    }
    else{
        return a.area>b.area;
    }
}
int find(int i)//查找
{
    if(i==fa[i])
    {
        return i;
    }
    else{
        fa[i]=find(fa[i]);
        return fa[i];
    }
}
void unio(int i,int j)//合并
{
    int x1=find(i);
    int x2=find(j);
    if(x1>x2)//需要编号最小的输出
    {
        fa[x1]=x2;
    }
    else 
    {
        fa[x2]=x1;
    }
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=N;i++)//初始化
    {
        fa[i]=i;
    }
    for(int i=0;i<n;i++)
    {
        int fat,mom;//父亲编号,母亲编号
        cin>>a[i].id>>fat>>mom;
        st[a[i].id]=1;
        if(fat!=-1)
        {
            unio(a[i].id,fat);//合并祖先
            st[fat]=1;
        }
        if(mom!=-1)
        {
            unio(a[i].id,mom);//合并祖先
            st[mom]=1;
        }
        int k;
        cin>>k;
        while(k--)
        {
            int idx;
            cin>>idx;
            if(idx!=-1)//当人没死才算在家庭成员里
            {
                unio(idx,a[i].id);
                st[idx]=1;
            }
        }
        cin>>a[i].fct>>a[i].area;
    }
    name jt[N];//记录家庭信息
    for(int i=0;i<n;i++)//统计家庭房产,面积
    {
        int id=find(a[i].id);
        jt[id].id=id;
        jt[id].area+=a[i].area;
        jt[id].fct+=a[i].fct;
    }
    for(int i=0;i<N;i++)//统计家庭成员
    {
        if(st[i]==1)//只有当人存在时
        {
            jt[find(i)].sum++;
        }
    }
    int cnt=0;//记录总家庭数
    int st2[N]={0};//不重复计算同一家庭
    name ans[N];//记录合并后的家庭情况
    for(int i=0;i<N;i++)
    {
        if(st[i]==1)
        {
            int id=find(i);
            if(st2[id]==0)
            {
                st2[id]=1;
                ans[cnt].fct=jt[id].fct/(1.0*jt[id].sum);
                ans[cnt].area=jt[id].area/(1.0*jt[id].sum);
                ans[cnt].id=id;
                ans[cnt++].sum=jt[id].sum;
                
            }
        }
    }
    cout<<cnt<<endl;
    sort(ans,ans+cnt,cmp);//按要求排序输出
    for(int i=0;i<cnt;i++)
    {
        printf("%04d %d %.3lf %.3lf\n",ans[i].id,ans[i].sum,ans[i].fct,ans[i].area);
    }
    return 0;
}

总结

灵活运用结构体,并查集

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值