描述
一个城市中有两个犯罪团伙A和B,你需要帮助警察判断任意两起案件是否是同一个犯罪团伙所为,警察所获得的信息是有限的。假设现在有N起案件(N<=100000),编号为1到N,每起案件由团伙A或团伙B所为。你将按时间顺序获得M条信息(M<=100000),这些信息分为两类:
1、 D [a] [b]
其中[a]和[b]表示两起案件的编号,这条信息表明它们属于不同的团伙所为
2、A [a] [b]
其中[a]和[b]表示两起案件的编号,这条信息需要你回答[a]和[b]是否是同一个团伙所为
注意你获得信息的时间是有先后顺序的,在回答的时候只能根据已经接收到的信息做出判断。
输入
第一行是测试数据的数量T(1<=T<=20)。
每组测试数据的第一行包括两个数N和M,分别表示案件的数量和信息的数量,其后M行表示按时间顺序收到的M条信息。
输出
对于每条需要回答的信息,你需要输出一行答案。如果是同一个团伙所为,回答”In the same gang.”,如果不是,回答”In different gangs.”,如果不确定,回答”Not sure yet.”。
样例输入
1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4
样例输出
Not sure yet.
In different gangs.
In the same gang.
我们先来讲一下这道题的思路,我们输入之后,首先将1~N这N个数字插入到并查集中,然后如果输入的是‘D’,那么就unionSet进行合并(一个集合内的所有元素代表这些犯罪团伙都不是一个团伙),如果输入的是‘A’,那么进行find函数查找a和b的所在集合,我们就只需要判断他们的所在集合是否相同即可。
如果不相同,代表不确定,输出Not sure yet.,如果a和b的父节点是相同的(代表他们是否是同一个集合),如果相同,就输出In the same gang.,如果不是就输出In different gangs.。
并查集和路径压缩算法的应用:
1、 father[i] 表示 i 的父节点是谁; relation[i] 表示 i 这个节点和他父节点的关系,0表示同一个犯罪组织,1表示不同。
2、初始化将每个节点的父节点赋值为自身, relation 也理所当然的赋值为0(相同)。
3、 Find(int x) 函数返回值为 x 的祖先。采用路径压缩。由于是递归,途中不断将这条路上上面节点接到祖先节点,并一路修改 relation 的值,直到最下面的节点接到祖先节点。
(1)如果 x 的父节点就是祖先节点,那么 father[x] 的 r 为 0,x 的 r 不需要改变;
(2)否则,有几种情况:
·r[ f[x] ] = 1,r[x] = 1,即 x 和他父亲不同,x 父亲和 x 父亲的父亲(祖先)也不同,所以 x 和 祖先相同
·r[ f[x] ] = 1,r[x] = 0,即 x 和他父亲不同,x 父亲和 x 父亲的父亲(祖先)相同,所以 x 和 祖先不同
·r[ f[x] ] = 0,r[x] = 1,即 x 和他父亲相同,x 父亲和 x 父亲的父亲(祖先)不同,所以 x 和 祖先不同
·r[ f[x] ] = 0,r[x] = 0,即 x 和他父亲相同,x 父亲和 x 父亲的父亲(祖先)也相同,所以 x 和 祖先相同
所以总结后有这样一个公式: r[x] = (r[x] + r[ f[x] ]) % 2;
4、输入命令时,如果是 D,表示这两个案件是不同团伙所做,那么把 x 的祖先归并到 y 的祖先下面,并修改 relation的值。由于查找祖先节点时,有路径压缩过程,所以归并时 x 已经直接接到他的祖先节点上。
由于 x 和 y 的种类不同,考虑如下情况:
(1)若 y 和 y 的祖先节点 fy 相同, r[y] = 0;则 x 和 fy不同。若 r[x] = 1, x 和 fx 不同,则 fx 和 fy 相同,
即 r[fx] = 0;同理可得当 r[x] = 0,r[fx] = 1;所以 r[fx] = 1 - r[x];
(2)若 y 和 fy 不同,同上分析,可得 r[fx] = r[x].
发现它,抓住它:
#include<bits/stdc++.h>
using namespace std;
template<class T>
struct DisjointSet{
int *parent;
T *data;
map<T,int> m;
int capacity;
int size;
DisjointSet(int max=1000){
capacity=max;
size=0;
parent=new int[max+1];
data=new T[max+1];
}
~DisjointSet(){
delete [] parent;
delete [] data;
}
bool insert(T x){
if(size==capacity) return false;
size++;
data[size]=x;
parent[size]=-1;
m[x]=size;
return true;
}
int getIndex(T x){
for(int i=1;i<=size;i++)
if(data[i]==x)
return i;
return -1;
}
int find(T x){
typename map<T,int>::iterator it;
it=m.find(x);
if(it==m.end()) return -1;
int i,rt;
i=rt=it->second;
while(parent[rt]>0)
rt=parent[rt];
return rt;
}
void unionSet(T x,T y){
int rx,ry;
rx=find(x);
ry=find(y);
if(rx==-1||ry==-1) return ;
if(rx==ry) return ;
if(parent[rx]<parent[ry]){
parent[rx]+=parent[ry];
parent[ry]=rx;
}
else{
parent[ry]+=parent[rx];
parent[rx]=ry;
}
find(x);
find(y);
}
};
int main(){
int t;
cin>>t;
while(t--){
DisjointSet<int> s;
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) s.insert(i);
for(int i=1;i<=m;i++){
int a,b;
char c;
cin>>c>>a>>b;
if(c=='D')
s.unionSet(a,b);
else{
if(s.find(a)!=s.find(b)) cout<<"Not sure yet.";
else if(s.parent[a]==s.parent[b]) cout<<"In the same gang.";
else cout<<"In different gangs.";
cout<<endl;
}
}
}
return 0;
}
这是一道并查集的经典题目,希望大家掌握。
该题链接:
OpenJudge - 3:发现它,抓住它http://dsalgo.openjudge.cn/retrieval/3/