并查集
简介:在一些有N个元素的集合应用问题中,通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找某个元素在哪个集合中。这类问题就需要用到并查集。
常用操作:
》初始化集合:每个节点开始的父节点都是本身
for(int i=1; i<=6; i++) father[i] = i;
》合并两个集合:将两个有关系的节点进行合并, 也就把这两个点所属的集合进行合并
void memage(int a, int b) //合并节点(a,b) { int a1 = find(a); // 找到a的父节点 int b1 = find(b); // 找到b的父节点 if(a1 != b1) //如果a与b的父节点不相同,则把a,b所属的集合合并 father[a1] = b1; }
》查询点:查找某个点的父节点,合并集合时需要用到~
int find(int x) { if(x != father[x]) //节点i的父节点不是本身,则需要找到它所属集合中的祖先祖先指(所属集合中 节点的父节点是本身的节点) fathre[x] = find(father[x]); //这样在查找的同时会沿途修改(父节点不是其祖先节点的节点)让其父节点成为其祖先节点,方便下一次查找 return father[x]; //返回父节点的值 }
大意:有两个帮派,
有两种操作:
》D a b 代表 a b不属于同一个帮派
》A a b 询问 a b什么关系(一个帮派,敌对帮派,不确定)
思路:
D a b表示两个人肯定是有联系的,因为两个人是敌是友都要靠彼此的条件来制约,把这样有关系的并在一起,作为一个集合。
敌人的敌人是朋友 这句话很经典的
证明后的结论:
在一个已经建好的一个集合里面,任意两个节点的关系由它们分别到根节点的距离决定,无论他们到根节点的距离是奇数还是偶数,只要是相同的(奇数与奇数~偶数与偶数)那么他们肯定是同一帮派,否则为不同帮派。
代码:
#include<iostream> #include<cstdio> using namespace std; #define N 100005 int fa[N]; int mark[N]; void first(int n) { for(int i=1; i<N; i++) //初始化并查集数组与标记数组 { fa[i] = i; mark[i] = 0; //0代表距离0 } } int find(int x) { if(fa[x] != x) { int temp = fa[x]; fa[x] = find(fa[x]); mark[x] = (mark[x] + mark[temp]) % 2; //当前节点与根节点的关系的更新~这里temp就是根节点。 } return fa[x]; } void memage(int x, int y) { int x1, y1; x1 = find(x); y1 = find(y); if(x1 != y1) { fa[x1] = y1; //合并两个集合 mark[x1] = (mark[x] + mark[y] + 1) % 2;//更新根节点改变的集合的关系(奇变偶,偶变奇) } } int main(){ int t; scanf("%d", &t); while(t--){ int i, n, m; scanf("%d%d", &n, &m); first(n); while(m--) { int a, b; char ch[2]; scanf("%s%d%d", ch, &a, &b); if(ch[0] == 'D') memage(a, b); else { if(find(a) != find(b)) printf("Not sure yet.\n");//必须在判断关系的最前面,有些集合只是更新了根结点关系,还未更新子节点关系 else if(mark[a] == mark[b]) printf("In the same gang.\n"); else printf("In different gangs.\n"); } } } return 0; }
思路:输入有特殊要求。注意处理特殊数据
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define M 100000 #define max(a, b) (a>b?a:b) #define min(a, b) (a>b?b:a) int f[M+1], flag; //flag记录是否有回路 int mark[M+1]; //记录迷宫的节点下标 void first() { for(int i=1; i<=M; i++) //初始化数组 { mark[i]=0; f[i]=i; } } int find(int x) { if(x != f[x]) f[x] = find(f[x]); return f[x]; } void memage(int a, int b) { int a1 = find(a); int b1 = find(b); if(a1 != b1) f[a1] = b1; if(a1 == b1) flag = 1; //表示flag标记a b两点之间有多条路径 } int main() { int n, m; while(scanf("%d%d", &n, &m) != EOF) { if(m == -1 && n == -1) break; if(n == 0 && m == 0) //这里有坑,特殊数据0 0 时输出为yes~~~这坑该怎么发现啊!!!!! { printf("Yes\n"); continue; } first(); int maxx=-1, minn=M+1, i; flag=0; //每组数据初始为0 while(m || n) //输入结束条件n m同时为0 { maxx= max(maxx, max(n, m)); //求迷宫内节点的最大下标与最小下标 minn= min(minn, min(n, m)); mark[n]=1; mark[m]=1; memage(n, m); scanf("%d%d", &n, &m); } if(flag) printf("No\n"); else //两点间没有多条路径,则检查是否全部节点记录 { for(i=minn, flag=0; i<=maxx; i++) if(mark[i] && f[i]==i) flag++; //如果属于一个集合,只有一个节点的根节点是本身 if(flag == 1) printf("Yes\n"); else printf("No\n"); } } return 0; }
大意:有n串字符,两个字符串首尾字符相同,就可以相连,是不是能够将所有的字符串连成一串。。
思路:
并查集检查是否能连成一串
数组检查 有字符没用到或者连的一串有分支~~
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int fa[30]; //并查集用 int f[30]; //记录各个字母个数 int mark[30]; //标记 int first() //初始化~~ { for(int i=0; i<=30; i++){ fa[i]=i; mark[i]=0; f[i]=0; } } int find(int a){ if(a != fa[a]) fa[a] = find(fa[a]); return fa[a]; } int main(){ int t, i; scanf("%d", &t); while(t--){ int n, a, b; scanf("%d", &n); while(n--){ char s[1005]; scanf("%s", s); a = s[0]-'a'; //26个字母用[0,25]表示 b = s[strlen(s)-1]-'a'; f[a]--; //a为每串子符的头字母 给它减一 f[b]++; //b为每串子符的尾字母 给它加一 头尾持平 mark[b] = 1; //标记 mark[a] = 1; fa[a] = fa[b] = find(a); //归入集合 } int in=0, out=0, other=0, flag=0; for(i=0; i<30; i++){ if(mark[i] && fa[i] == i) flag ++; //集合个数 if(mark[i] && f[i] ==-1) in++; //剩余头字母个数 if(mark[i] && f[i] == 1) out++;//剩余尾字母个数 if(mark[i] && (f[i] <-1 || f[i] > 1) ) other++; //一种字母剩余多个~~ } if(flag == 1) //所有的头字母尾字母都在一个集合 { if((in==1 && out==1 && other == 0)||(in==0&&out==0&&other==0)) printf("Ordering is possible.\n"); else printf("The door cannot be opened.\n"); } else printf("The door cannot be opened.\n"); } return 0; }

浙公网安备 33010602011771号