[kuangbin带你飞]专题五 并查集 题解+总结

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/JiangHxin/article/details/102729765

kuangbin带你飞:点击进入新世界

总结:

本人算是初学者中的初学者,欢迎交流~
并查集的接触过的不多,大概只有普通并查集,带权并查集,种族并查集,传说中的可持续化并查集只是听说过还没有接触,不过种族并查集可以用带权并查集来做,带权的话又常常跟dp联系在一起,普通并查集可以作为其他算法的一项工具。
kuangbin的题型:
A.普通并查集 参考1.2.3.6.9
B.带权并查集 参考4.5.7.8
C.并查集+DP

kuangbin之外:
[并查集模板]:待更

1.Wireless Network

原题链接:传送门

思路:

  1. 两点之间的距离预处理map[i][j]表示是否能够通信。
  2. 指令是o , 要先维护后才能通信。
  3. 维护一个点之后判断有没有可以其他可以进行通信的点,如果有就加入该点的集合。

    代码如下:
#include<iostream>
#include<cstdio>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=1e3+5;
int a[manx],x[manx],y[manx],vis[manx];
bool map[manx][manx];
int n,d,u,v,l,r;
int find(int f)
{
    if(a[f]==f) return f;
    return a[f]=find(a[f]);
}
int main()
{
    cin>>n>>d;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&x[i],&y[i]);
        a[i]=i;
    }
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            if((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])<=d*d)
                map[i][j]=map[j][i]=true;
    char c;
    while(cin>>c)
    {
        if(c=='O'){
            scanf("%d",&l);
            vis[l]=1;
            for(int r=1;r<=n;r++)
            {
                if(r!=l&&map[l][r]&&vis[r])
                {
                    u=find(l),v=find(r);
                    if(u==v) continue;
                    a[u]=v;
                }
            }
        }
        else if(c=='S'){
            scanf("%d%d",&l,&r);
            l=find(l),r=find(r);
            if(l==r) printf("SUCCESS\n");
            else printf("FAIL\n");
        }
    }
    return 0;
}


2.The Suspects

原题链接:传送门

思路:

  1. 模板题,一步一步加入集合,最后判断有多少人跟0在同一个集合就行了。

    代码如下:
#include<iostream>
#include<cstdio>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=3e4+5;
int a[manx];
int find(int x)
{
    if(a[x]==x) return x;
    return a[x]=find(a[x]);
}
int main()
{
    int n,m,u,v;
    while(cin>>n>>m)
    {
        if(n==0&&m==0) break;
        for(int i=0;i<n;i++) a[i]=i;
        while(m--)
        {
            int q;
            cin>>q>>u;
            for(int i=1;i<q;i++){
                cin>>v;
                a[find(u)]=find(v);
                u=v;
            }
        }
        int ans=0;
        for(int i=0;i<n;i++)
            if(find(a[i])==find(0))
                ans++;
        cout<<ans<<endl;
    }
    return 0;
}


3.How Many Tables

原题链接:传送门

思路:

  1. 求集合的个数,在连接的时候进行判断即可。

    代码如下:
#include<iostream>
#include<cstdio>
using namespace std;
const int manx=1e3+5;
int a[manx];
int n,m;
int find(int x)
{
    if(a[x]==x) return x;
    else return a[x]=find(a[x]);
}
void show()
{
    for(int i=1;i<=n;i++)
        a[i]=i;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        scanf("%d %d",&n,&m);
        int ans=0;
        show();
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d %d",&u,&v);
            u=find(u),v=find(v);
            if(u!=v) ans++;
            a[u]=v;
        }
        printf("%d\n",n-ans);
    }
    return 0;
}


4.How Many Answers Are Wrong

原题链接:传送门

思路:

  1. 给一些区间以及其区间和,判断假话语句数量。
  2. 带权并查集的模板题,根据输入连点,如果已经连好线,判断权值是否相等。
  3. 关于带权并查集可参考文章开头的模板,欢迎交流。

    代码如下:
#include<iostream>
#include<cstdio>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=3e5+5;
int a[manx],d[manx];
int find(int x)
{
    if(a[x]==x) return x;
    int fa=find(a[x]);
    d[x]=d[x]+d[a[x]];
    return a[x]=fa;
}
int main()
{
   int n,m;
   while(scanf("%d%d",&n,&m)!=EOF)
   {
        int ans=0;
        for(int i=0;i<=n;i++) a[i]=i,d[i]=0;
        for(int i=1;i<=m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            u--;
            int x=find(u),y=find(v);
            if(x==y&&d[v]-d[u]!=w) ans++;
            else {
                if(x<y) a[y]=x,d[y]=d[u]+w-d[v];
                else a[x]=y,d[x]=d[v]-w-d[u];
            }
        }
        cout<<ans<<endl;
   }
   return 0;
}


5.食物链

原题链接:传送门

思路:

  1. 同上题,带权并查集入门题目,也可以说是种族并查集,那么只要对权值进行取模即可。
  2. 注意更新权值的时候需要+3再取模,防止出现负数。

    代码如下:
#include<iostream>
#include<cstdio>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=3e5+5;
int a[manx],d[manx];
int find(int x)
{
    if(a[x]==x) return x;
    int fa=find(a[x]);
    d[x]=(d[x]+d[a[x]])%3;
    return a[x]=fa;
}
int main()
{
    int n,m,ans=0;
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n;i++) a[i]=i,d[i]=0;
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&w,&u,&v);
        w--;
        if(u>n||v>n||(w==2&&u==v)) ans++;
        else {
            int x=find(u),y=find(v);
            if(x==y&&(d[v]-d[u]+3)%3!=w) ans++;
            else {
                a[y]=x;
                d[y]=(d[u]+w-d[v]+3)%3;
            }
        }
    }
     cout<<ans<<endl;
    return 0;
}


6.Supermarket

原题链接:传送门

思路:

  1. 这道题本来是用贪心来做的,这里参考kuangbin大佬的做法,将并查集作为一个工具,记录每个商品至多能在什么时候卖出。

    代码如下:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;
const int manx=1e4+5;
int a[manx];
struct node{
    int w,id;
}d[manx];
bool cmp(node a,node b){
    return a.w>b.w;
}
int find(int x)
{
    if(a[x]==-1) return x;
    else return a[x]=find(a[x]);
}
int main()
{
    int n;
    while(cin>>n){
        for(int i=1;i<=n;i++) cin>>d[i].w>>d[i].id;
        memset(a,-1,sizeof(a));
        sort(d+1,d+1+n,cmp);
        int ans=0;
        for(int i=1;i<=n;i++){
            int p= find(d[i].id);
            if(p>0){
                ans+=d[i].w;
                a[p]=p-1;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}


7.Parity game

原题链接:传送门

思路:

  1. 跟上面那道区间和相关的带权并查集差不多,不过这里只有奇偶两种情况,那么就是对2取模,即只有01两种情况,因此可以直接异或。
  2. 主要问题还是多了离散化,数据过大无法创建那么大的节点数组,因此可以把数据映射到一个新的数组里面。

    代码如下:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;
const int manx=1e4+5;
struct node{
    int l,r;
    char s[10];
}q[manx];
int a[manx],d[manx],f[manx];
int find(int x)
{
    if(a[x]==x)return x;
    else{
        int root=find(a[x]);
        d[x]^=d[a[x]];
        return a[x]=root;
    }
}
int main(){
    int n,m,k=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%s",&q[i].l,&q[i].r,&q[i].s);
        q[i].l--;
        f[++k]=q[i].l,f[++k]=q[i].r;
    }
    sort(f+1,f+1+k);
    k=unique(f+1,f+1+k)-f-1;
    for(int i=1;i<=k;i++) a[i]=i,d[i]=0;
    int i;
    for(i=1;i<=m;i++)
    {
        int l=lower_bound(f+1,f+1+k,q[i].l)-f;
        int r=lower_bound(f+1,f+1+k,q[i].r)-f;
        int u=find(l),v=find(r);
        int w=0;
        if(q[i].s[0]=='o') w=1;
        if(u==v&&d[l]^w!=d[r]) break;
        else if(u<v) a[u]=v,d[u]=d[l]^d[r]^w;
        else if(u>v) a[v]=u,d[v]=d[l]^d[r]^w;
    }
    cout<<i-1<<endl;
    return 0;
}


8.A Bug’s Life

原题链接:传送门

思路:

  1. 跟上道题一样,只有两种情况,一种同性恋,一种异性恋,所以可以用xor来维护权值数组。

    代码如下:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
const int manx=2e4+5;
int a[manx],d[manx];
int find(int x)
{
    if(a[x]==x) return x;
    else {
        int fa=find(a[x]);
        d[x]^=d[a[x]];
        return a[x]=fa;
    }
}
int main()
{
    int t,T=1;
    scanf("%d",&t);
    while(t--)
    {
        int n,m,flag=0;
        scanf("%d%d",&n,&m);
        for(int i=0;i<=n;i++) a[i]=i,d[i]=0;
        for(int i=1;i<=m;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            if(flag) continue;
            int fu=find(u),fv=find(v);
            if(fu==fv){
                if(d[u]==d[v])
                    flag=1;
            }
            else a[fv]=fu,d[fv]=d[u]^d[v]^1;
        }
        cout<<"Scenario #"<<T++<<":"<<endl;
        if(flag) puts("Suspicious bugs found!");
        else puts("No suspicious bugs found!");
        cout<<endl;
    }
    return 0;
}


9.Is It A Tree?

原题链接:传送门

思路:

  1. 连通无环,即点数比边数大一。

    代码如下:
#include<iostream>
#include<set>
#include<cstring>
#include<cstdio>
using namespace std;
int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        if(n==-1&&m==-1) break;
        if(!n&&!m){
            puts("Yes");
            continue;
        }
        set<int>q;
        q.insert(n),q.insert(m);
        int ans=1;
        while(cin>>n>>m)
        {
            if(!n&&!m) break;
            ans++;
            q.insert(n),q.insert(m);
        }
        if(ans==q.size()-1)  puts("Yes");
        else  puts("No");
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/JiangHxin/article/details/102729765