并查集:
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
8.并查集的删除
***************************************************************************************
还没有看懂,只是大体上了解了一下经过。。。研究透了再补上。
合纵连横
9.并查集和其他各个用法的结合
未完待续~~~~~~