算法学习——并查集

模板题

活动 - AcWing

find 函数不仅有找祖宗的功能,还把这个查找路径上所有节点直接变成了祖宗节点的孩子

#include<iostream>

using namespace std;

const int N=100010;
int p[N];//定义多个集合

int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    /*
    经上述可以发现,每个集合中只有祖宗节点的p[x]值等于他自己,即:
    p[x]=x;
    */
    return p[x];
    //找到了便返回祖宗节点的值
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) p[i]=i;
    while(m--)
    {
        char op[2];
        int a,b;
        scanf("%s%d%d",op,&a,&b);
        if(*op=='M') p[find(a)]=find(b);//集合合并操作
        else
        if(find(a)==find(b))
        //如果祖宗节点一样,就输出yes
        printf("Yes\n");
        else
        printf("No\n");
    }
    return 0;
}

字符串,用map变形的题目

一中校运会之百米跑 - 洛谷

#include<bits/stdc++.h>
using namespace std;
const int N=2e4+10;;
int p[N];//定义多个集合
map<string,int>mmp;

int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        string s;cin>>s;
        mmp[s]=i;
    }
  
    for(int i=1;i<=n;i++) p[i]=i;

    while(m--)
    {
        string s1,s2;
        cin>>s1>>s2;
        p[find(mmp[s1])]=find(mmp[s2]);//集合合并操作
    }
    int k;
    cin>>k;
    while(k--)
    {
        string s1,s2;
        cin>>s1>>s2;
        if(find(mmp[s1])==find(mmp[s2]))cout<<"Yes."<<endl;
        else cout<<"No."<<endl;
    }
    return 0;
}

[蓝桥杯 2017 国 C] 合根植物 - 洛谷

在最后寻找不同并查集个数的地方升级了

#include<bits/stdc++.h>
using namespace std;
const int N=2e7+10;;
int p[N];//定义多个集合

int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);

    for(int i=1;i<=n*m;i++) p[i]=i;
    int t;cin>>t;
    while(t--)
    {
        int s1,s2;
        cin>>s1>>s2;
        p[find(s1)]=find(s2);//集合合并操作
    }

    int ans=0;
    for(int i=1;i<=n*m;i++)
    {
        if(p[i]==i)ans++;
    }
    cout<<ans;
    // vector<int>a(n*m+1);

    // for(int i=1;i<=n*m;i++)
    // {
    //     a.push_back(find(i));
    // }
    // sort(a.begin(),a.end());
    // a.erase(unique(a.begin(),a.end()),a.end());
    // cout<<a.size()-1<<endl;//去除0
    return 0;
}

[NOIP2017 提高组] 奶酪 - 洛谷

这里的并查集直接将相交的球合并起来了,最后判断上下表面的球所在的集合有没有相连

#include<bits/stdc++.h>
using namespace std;//不加本代码爆零
const int N=100001;
typedef long long LL;
int p[1001];//并查集
LL x[N],y[N],z[N];
int find(int x)
{
    //如果x的指向不是
    if(x!=p[x])p[x]=find(p[x]);
    return p[x];
}
//两点距离公式,注意这里算的是距离平方。
LL dis(long long x,long long y,long long z,long long x1,long long y1,long long z1){
    return (x-x1)*(x-x1)+(y-y1)*(y-y1)+(z-z1)*(z-z1);
}

//分别记录 与上表面相交的洞的编号和与下表面相交的洞的编号
int cnt_up[N],cnt_down[N];

int main()
{
    int T;
    cin>>T;
    LL n,h,r;
    while(T--)
    {
        cin>>n>>h>>r;
        int tot1=0;
        int tot2=0;
        //并查集初始化
        for(int i=1;i<=n;i++)p[i]=i;

        for(int i=1;i<=n;i++)
        {
            cin>>x[i]>>y[i]>>z[i];
            //判断这个点是否与上表面相交
            if(z[i]+r>=h)
            {
                //记录与上表面相交的序号
                cnt_up[++tot1]=i;
            }
            //判断这个点是否与下表面相交
            if(z[i]-r<=0)
            {
                //记录与上表面相交的序号
                cnt_down[++tot2]=i;
            }
            //枚举之前的洞是否与当前的洞相交,相交的话并查集合并
            for(int k=1;k<=i;k++)
            {
                //判断边界
                //if ((x[i]-x[k])*(x[i]-x[k])+(y[i]-y[k])*(y[i]-y[k])>4*r*r) continue;
                if(dis(x[i],y[i],z[i],x[k],y[k],z[k])<=4*r*r)
                {
                    if(find(i)!=find(k))//前提条件
                    {
                        //并查集的合并
                        p[find(i)]=find(k);
                    }
                }
            }


        }

        int ans=0;
        //循环遍历
        for(int i=1;i<=tot1;i++)
        {
            for(int j=1;j<=tot2;j++)
            {
                if(find(cnt_up[i])==find(cnt_down[j]))
                {
                    ans=1;
                    break;
                }
            }
            if(ans==1)break;
        }
        if(ans==1)cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;
}

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值