并查集的基础用法

并查集:

1.小希的迷宫

题目大意:小希希望任意两个房间有且仅有一条路径可以相通(除非走了回头路)。小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路。

思路:利用并查集。

①如果合并前两个房间已连通,不符合题意

②然后全部合并一圈后再判断是否只有一个根节点。就是fa[i]=-1的点有多少个(下面代码中是set[i]==i的点),如果不是1那么说明还有没有连通的点。

代码

 

2.一笔画问题

题意:看图是否能一笔画下来

思路:看图是否连通。如果连通的话,再看是否是一个通路或者是一个回路。利用欧拉路的判断方法,通过度来判断。如果都是偶数度说明是一个回路,如果恰好有两个点是奇数度,则说明是一个通路,且奇数度的两个点一个是入口一个是出口。

如果是通路的话,利用并查集,判断一下合并之后的根节点有几个。一个的话说明可以。如果是多个的话直接输出no;

代码

3.is it a tree?

题目大意:判断给定的点构成的是不是一棵树

思路:①判断是否连通

          ②不成环

          ③每个节点的入度只能是1

代码:

#include
#include
#define max 1000+10
int set[max],in[max];//set记录父节点,in记录入度 
int find(int p)
{
    int child=p;
    int t;
    while(p!=set[p])
    p=set[p];
    while(child!=p)
    {
        t=set[child];
        set[child]=p;
        child=t;
    }
    return p;
}
void merge(int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    if(fx!=fy)
    set[fx]=fy;
}
int main()
{
    int i,j,a,b,exist,t=1,sum,wrong;
    while(1)
    {
        memset(set,0,sizeof(set));
        memset(in,0,sizeof(in)); 
        exist=0;
        while(scanf("%d%d",&a,&b)&&(a!=0||b!=0))
        {
            if(a<0&&b<0)
            return 0;
            if(set[a]==0) set[a]=a;
            if(set[b]==0) set[b]=b;
            in[b]++;
            if(find(a)==find(b)) exist=1;//用来判断是否有环
            merge(a,b);
        }
        if(exist)
        {
            printf("Case %d is not a tree.\n",t++);
        }
        else
        {
            sum=0;wrong=0;
            for(i=1;i1)//没有连通 
                    {
                        wrong=1;
                        break;
                    }
                } 
                if(in[i]>1)//入度大于1
                {
                    wrong=1;
                    break;
                } 
            }
            if(wrong)
            printf("Case %d is not a tree.\n",t++);
            else
            printf("Case %d is a tree.\n",t++);
        }
    }
    return 0;
}

4.用并查集来判断环的个数

思路:重点看代码:sum

void merge(int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    if(fx!=fy)
    set[fx]=fy;
    else
    sum++;
}

代码

5.求所有树中最多的节点数

思路:就是在合并的时候,将子树已有的num加在集合里,然后比较sum。初始化每一个节点的num为1

void merge(int x,int y)  
{  
    int fx=find(x);  
    int fy=find(y);  
    if(fx!=fy)  
    {  
        set[fx]=fy;  
        path[fy]+=path[fx];//这里不需要判断大小   
        if(sum

代码

6.find them,Catch them[带权并查集]

题意:已知有两个犯罪团伙,A  x y的意思是查询x和y是否在同一犯罪团伙;D  x  y的意思是x和y属于不同的犯罪团伙。

思路:因为一共就两个犯罪团伙,因此可以将一个点x的对立点设为x+n,那么当两个点(x,y)属于不同的犯罪团伙时,就可以直接将x+n和y设为一个集合,y+n和x设为一个集合。

反正就俩犯罪团伙,标记set[a]和set[a+n]分别是两个犯罪团伙。

则有下列代码:

#include 
#include 
#define MAX 200000+10
using namespace std;
int set[MAX];
int n, m;
void init()
{
    for(int i = 1; i <= 2*n; i++)
    set[i] = i;
}
int find(int p)
{
    int t;
    int child = p;
    while(p != set[p])
    p = set[p];
    while(child != p)
    {
        t = set[child];
        set[child] = p;
        child = t;
    }
    return p;
}
void merge(int x, int y)
{
    int fx = find(x);
    int fy = find(y);
    if(fx != fy)
    set[fx] = fy;
}
bool same(int x, int y)
{
    return find(x) == find(y);
}
void slove()
{
    int x, y; 
    char a[2];
    while(m--)
    {
        scanf("%s%d%d", a, &x, &y);
        if(a[0] == 'D')
        {
            merge(x+n, y);
            merge(x, y+n);
        }
        else if(a[0] == 'A')
        {
            if(same(x, y))
            printf("In the same gang.\n");
            else if(same(x, y+n))
            printf("In different gangs.\n");
            else
            printf("Not sure yet.\n");
        } 
    }
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        init();
        slove();
    }
    return 0;
} 

7.virtual Friends

 

题目大意:给出n组关系,每组关系有两个人的名字,表示这两个人是朋友。 遵循原则:你的朋友的朋友也是你的朋友。需要你在输入每组关系的同时  输出在这对关系的前提下有多少个人刚刚成为朋友。(举个例子:这里的朋友关系很容易让人产生误解,主要就是比如集合5和集合6的成为了朋友,那么就多加了11个好朋友)

思路:还是应用并查集,在其中进行加的操作。

代码:

#include 
#include 
#include 
#include 
#define MAX 200000+1
using namespace std;
int set[MAX], fri[MAX];//set 存储父节点 fri 记录朋友数目 
map rec;
void init()
{
	for(int i = 1; i < MAX; i++)
	{
		set[i] = i;
		fri[i] = 1;//初始自己一个朋友 
	} 
} 
int find(int p)
{
	int t;
	int child = p;
	while(p != set[p])
	p = set[p];
	while(child != p)
	{
		t = set[child];
		set[child] = p;
		child = t;
	}
	return p;
}
int merge(int x, int y)
{
	int fx = find(x);
	int fy = find(y);
	if(fx != fy)
	{
		//fy 当作父节点 
		set[fx] = fy;
		fri[fy] += fri[fx];
	}
	return fri[fy];
}
int main()
{
	int t;
	int n;
	char a[30], b[30];
	while(scanf("%d", &t) != EOF)
	{
		while(t--)
	    {
	    	scanf("%d", &n);
		    init();
		    int num = 1;
		    rec.clear();
		    while(n--)
		    {
		    	scanf("%s%s", a, b);
			    if(!rec[a]) rec[a] = num++;
			    if(!rec[b]) rec[b] = num++;
			    printf("%d\n", merge(rec[a], rec[b]));
		    } 
	    }
	}
	return 0;
}

8.并查集的删除

***************************************************************************************

还没有看懂,只是大体上了解了一下经过。。。研究透了再补上。

合纵连横

9.并查集和其他各个用法的结合

未完待续~~~~~~

 

 

你可能感兴趣的:(赛前看的博客,图论——并查集)