[kuangbin带你飞]专题五 并查集
Wireless Network POJ - 2236
题意:n台电脑,坐标(x,y),电脑通讯范围为d;一开始,给出所有电脑坐标,然后所有电脑初始状态都是坏的,题目输入两个操作,第一修电脑且这台电脑可对d范围内正常电脑进行通讯了;第二就是查询,两台电脑是否可以通讯?
算法:并查集
思路:第一次,我直接通过坐标判断,那些电脑之间存在可通讯路径,存储起来,然后每次修电脑就激活合并并查集,然后查询的时候,再查两台电脑的父亲是否一样?然后TLE了。后面我把路径判断放在了修电脑操作里面了,就AC了
难点:这里要注意就是,修了的电脑才能正常通讯,坏电脑根本不运作;然后就是上面提到的TLE问题。
查看代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
const int INF = 0x3f3f3f3f;
int fa[1010];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void unity(int x,int y){fa[find(y)] = find(x);}
int n,e;
int pX[1010],pY[1010];
bool ck[1010];
bool check(int a,int b,int c,int d){return (c-a)*(c-a)+(d-b)*(d-b) <= e*e;}
int main(){
cin>>n>>e;
for(int i=1;i<=n;i++) fa[i]=i,cin>>pX[i]>>pY[i];
char ch;
while(cin>>ch){
int a,b;
if(ch=='O'){
cin>>a;ck[a] = true;
for(int i=1;i<=n;i++) if(ck[i]&&check(pX[a],pY[a],pX[i],pY[i])) unity(a,i);
}else{
cin>>a>>b;
if(find(a)==find(b)) puts("SUCCESS");
else puts("FAIL");
}
}
}
The Suspects POJ - 1611
题意:多个测试用例,n个学生,m个小组,每个小组里面的人只要有一人,是潜在病毒携带者,其他人都要遭殃;学生0已经确定是了。然后问你有多少个学生是可能的病毒携带者。
算法:并查集
思路:每个组的学生都进行并查集合并,将他们父亲变成同一个祖先,最后查一下,有那些学生是和学生0同一个祖先就行了
查看代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
const int INF = 0x3f3f3f3f;
int fa[30010];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void unity(int x,int y){fa[find(y)] = find(x);}
int main(){
int n,m;
while(cin>>n>>m){
if(n==0&&m==0) break;
for(int i=0;i<n;i++) fa[i] = i;
while(m--){
int _;cin>>_;
int a;cin>>a;_--;
while(_>0&&_--){
int b;cin>>b;
unity(a,b);
}
}
int a = find(0),ans=0;
for(int i=0;i<n;i++) if(find(i)==a) ans++;
cout<<ans<<endl;
}
}
How Many TablesHDU - 1213
题意:给你n个人,m个关系,然后有关系的人可以安排在同一桌,问最少需要多少桌开饭?
算法:并查集
思路:和上题大差不差,并查集,合并然后查询就行了。
查看代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
const int INF = 0x3f3f3f3f;
int fa[1010];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void unity(int x,int y){fa[find(y)] = find(x);}
int main(){
int _;cin>>_;
while(_--){
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++) fa[i] = i;
while(m--){
int a,b;cin>>a>>b;
unity(a,b);
}
int ans = 0;
for(int i=1;i<=n;i++) if(fa[i] == i) ans++;
cout<<ans<<endl;
}
}
How Many Answers Are WrongHDU - 3038
题意:a玩家自己写一串数字串,然后b玩家开始问连续子串的和,a玩家在逻辑冲突前的回答都正确——即当出现两个答案不同的时候,第一个出现的是正确答案。
算法:带权并查集
思路:看这题区间和,就想到区间树,但是一看不对阿,这题是并查集,然后百度一下,是新东西(带权并查集),通过大牛博客学习:https://blog.csdn.net/hzf0701/article/details/109003395;接下来是我的理解,你把数组value,理解成向量,然后再看代码就豁然开朗;
难点:第一就是带权并查集的理解;第二就是区间(这里不是很懂,借鉴别人博客https://www.cnblogs.com/fighlone/p/13526864.html),必须左开右闭 或者 左闭右开
查看代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 200000 + 10;
int fa[MAXN];
int value[MAXN];
int ans;
int find(int x){
if(x==fa[x]) return x;
else{
int temp = fa[x];
fa[x] = find(fa[x]);
value[x] += value[temp];
return fa[x];
}
}
void unity(int x,int y,int v){
int faX = find(x),faY = find(y);
if(faX != faY){
fa[faX] = faY;
value[faX] = -value[x]+value[y]+v;
}else{
if(value[x]-value[y]!=v) ans++;
}
}
int main(){
int n,m;
while(cin>>n>>m){
ans = 0;
for(int i=1;i<=n+1;i++) fa[i] = i , value[i] = 0;
while(m--){
int a,b,c;cin>>a>>b>>c;
b++;
unity(a,b,c);
}
cout<<ans<<endl;
}
}
食物链 POJ - 1182
题意:
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。
这题与上面那题一样,先出现先正确,后出现冲突后错误;
算法:带权并查集/并查集
思路:一开始我用带权并查集那种向量理解进去,假设,动物A吃B,那么A指向B的向量就是1,如果动物A与D同类,那么A与D向量就是0;但是你会发现一个问题,A吃B,B吃C,那么A指向C的向量就是2,但是实际上A指向C的向量是-1,因为C吃A,C指向A是1;所以现在问题就是如何把向量2与向量-1划等号呢?那就是+3取3余数,保证所有向量都在0,1,2之间;0表示同类,1表示吃对方,2表示被对方吃;
难点:就是只有一个输入用例,你这里多输入就wrong;
查看代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 50000 + 10;
int fa[MAXN];
int value[MAXN];
int ans;
int find(int x){
if(x==fa[x]) return x;
else{
int temp = fa[x];
fa[x] = find(fa[x]);
value[x] += value[temp];
value[x]%=3;
return fa[x];
}
}
void unity(int x,int y,int v){
int faX = find(x),faY = find(y);
if(faX != faY){
fa[faX] = faY;
value[faX] = (3-value[x]+value[y]+v)%3;
}else{
if(v==0){
if(value[x]!=value[y]) ans++;
}else{
if((value[x]-value[y]+3)%3!=1) ans++;
}
}
}
int main(){
int n,m;scanf("%d%d",&n,&m);
ans = 0;
for(int i=1;i<=n;i++) fa[i] = i , value[i] = 0;
while(m--){
int a,b,c;scanf("%d%d%d",&a,&b,&c);
if(b>n||c>n) ans++;
else if(a==2&&b==c) ans++;
else if(a==1&&b==c) continue;
else unity(b,c,a-1);
}
printf("%d\n",ans);
}
第二种思路,就是建图加并查集,这里是从其他博客看来,我分享一下:https://blog.csdn.net/lisong_jerry/article/details/80029967