码迷,mamicode.com
首页 > 其他好文 > 详细

并查集合集

时间:2019-03-12 11:58:53      阅读:144      评论:0      收藏:0      [点我收藏+]

标签:number   block   大小   说明   顶点   ==   com   src   条件   

之前的并查集的题目的整合

 

技术图片

并查集可以说是数据结构里比较简单的一种了,这一道题我看了很多人的题解

大致有两种做法:

1.建立一个3*n大小的数组,将这些动物放在这三个范围里面n,2*n,3*n,然后进行判断和合并

2.利用“向量”的思想/将同类,被捕食,捕食设置为0,1,2然后进行关系的改变

kuangbin的做法: http://www.cnblogs.com/kuangbin/archive/2013/04/02/2996358.html

最简单的应该是第一种

技术图片
 1 //方法1
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<cstdio>
 6 using namespace std;
 7 const int N=5e4+10;
 8 int n, k, d, x, y,ans;
 9 int f[N*3+1]; //r=relative;f=father
10 void init(){
11     for (int i = 1; i <= n*3;i++)
12         f[i] = i;
13 }//开一个3*n的father数组,也就是f[];
14 int find(int x){
15     return f[x] == x ? x : f[x] = find(f[x]);
16 }
17 bool issame(int a,int b){
18     return find(a) == find(b);
19 }
20 void unionset(int x,int y){
21     x = find(x), y = find(y);
22     if(x==y)
23         return;
24     f[y] = x;
25 }
26 int main(){
27     //IOS
28     scanf("%d%d", &n, &k);
29     init();
30     while(k--){
31         scanf("%d%d%d", &d, &x, &y);
32         if(x>n||y>n||y<=0||x<=0||(d!=1&&d!=2)){
33             ans++;
34             continue;
35         }
36         if(d==1){
37             if(issame(x,y+n)||issame(x+n,y))
38                 ans++;
39             else
40                 unionset(x, y),unionset(x + n, y + n),unionset(x + 2 * n, y + 2 * n);//这样合并的意思是:如果x为A类,则y也为A类;如果x为B类,y也为B类;C类同理,但现在并没有确定谁是哪一类
41         }//只是说明,不管x,y属于哪一类,他们两个都是在同一类里面,是共祖的
42         if(d==2){
43             if(issame(x,y)||issame(x,y+2*n))
44                 ans++;
45             else
46                 unionset(x, y + n), unionset(x + n, y + 2 * n), unionset(x + 2 * n, y);//这样合并的意思就是,仍然不管x属于哪个种类,但是x一定要处于y的食物链上端1级
47 48     }
49     cout << ans;
50     //system("pause");
51     return 0;
52 }
技术图片

 

所以我们进行的一系列的合并或者是捕食关系的设置,都是将这些动物的相对位置进行改变,

如果两个动物都还没有被设置过,或者只有一个动物被设置过,那么第37行/第43行的条件就不会成立,所以不用担心会有冲突的情况

 

那么,需不需要再判断issame(x+n,y+2*n)这一类的条件,是否需要把每个情况都列举出来?

这个是不用的,因为只要两个都没被设置过,或者是只有一个被设置过,那么可以直接进入unionset环节,也就是第40行或者第46行,

40:unionset(x, y),unionset(x + n, y + n),unionset(x + 2 * n, y + 2 * n);
46:unionset(x, y + n), unionset(x + n, y + 2 * n), unionset(x + 2 * n, y);

这两行的操作兼顾了所有的情况,也就是:

假如x属于A,那么y应该是______;

假如x属于B,那么y应该是______;

假如x属于C,那么y应该是______;

 

这样,x与y的相对位置始终是确定的;

判断的时候也就不会出错

 

我看到有好几个这种方法的题解里面,都有人用了一个rk[n*3]数组,但这个数组删去,同样可以AC,这个rank数组的作用是判断捕食关系,但其实这个数组是不需要的,

因为题设里面已经说明,当d==2的时候,需要判断是否是x捕食y,那么只需要利用issame函数判断是否是y捕食x,如果是,则谎言数量+1,如果不是,进入unionset环节,进行相对位置的改变

 

 

 

 

我爱并查集

图论和并查集是好朋友吗

技术图片

首先我们要知道,这题里面说的成环,是说一个比较孤独的环 ~不能乱七八糟连一大堆其他的不能成环的顶点
所以一个环里,每个顶点的度为2
(如果可以有乱七八糟的点,怎么办呢?我觉得那应该要用到dfs?回头仔细想一想)
这一题并查集就可以做

可以union的条件:
条件1:两点相连
条件2:两点的度均为2

这个时候就可以union
如果它们本来就共祖,那么说明成环了,直接ans++;
(在这个union函数里面,还可以再加上一个member数组,用来存放该环的周长,但这题不用考虑)

不愧是模板题~

上代码:

#include<bits/stdc++.h>
using namespace std;
//typedef long long ll;
const int N=2e5+10;
int ans,n,m;
int deg[N];
struct edge{
    int a,b;
}e[N];
int f[N];//father,ahhahahahhhahahaahah并查集!!!!!!

int find(int x){return f[x]==x?x:f[x]=find(f[x]);}//查
void unionset(int x,int y){//并
    int r1=find(x),r2=find(y);
        if(r1!=r2) f[r2]=r1;
        else ans++;
}

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

    for(int i=1;i<=m;i++) f[i]=i;//初始化

    for(int i=0;i<m;i++){
        scanf("%d%d",&e[i].a,&e[i].b);
        deg[e[i].a]++;
        deg[e[i].b]++;
    }//has its order!!!!!!!!!

    for(int i=0;i<m;i++){
        if(deg[e[i].a]==2&&deg[e[i].b]==2)//有两条边!!is OK!!!
            unionset(e[i].a,e[i].b);
    }
        cout<<ans;
        return 0;
}
 

并查集合集

标签:number   block   大小   说明   顶点   ==   com   src   条件   

原文地址:https://www.cnblogs.com/guaguastandup/p/10515605.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!