发现它,抓住它

描述
一个城市中有两个犯罪团伙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/ 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙星尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值