犯罪团伙
【题目描述】
警察抓到了n个罪犯,警察根据经验知道他们属于不同的犯罪团伙,却不能判断有多少个团伙,但通过警察的审讯,知道其中的一些最烦之间
相互认识,有可能一个犯罪团伙只有一个人,请你根据已知罪犯之间的关系,确定犯罪团伙的数量。已知罪犯的编号从1至n
【输入】
第一行:n罪犯数量(n<=1000)
第二行:m关系数量(m<5000)
以下若干行:每行两个数,i和j,中间一个空格隔开,表示罪犯i和罪犯j相互认识
【输出】
一个整数,犯罪团伙的数量
【胡乱分析】
这是一道水题,主要是看它训练的知识点:并查集和深搜。那么代码怎么实现呢
[并查集]
并查集支持三个操作MAKE,UNIONN,FIND
递归的实现find
int find(int x) { if(father[x] != x) return find(father[x]); else return x; }
但是如果单链很长的情况下,这种方法会超时,因此进行路径压缩,把所有父亲指针均指向根节点
int find(int x) { if(father[x] != x) return father[x] = find(father[x]); return father[x]; }
将集合合并:
void unionn(int r1,int r2) { father[r2] = r1; }
判断两个元素是否在同一集合:
bool judge(int x,int y) { x = find(x); y = find(y); if(x == y) return true; else return false; }
【代码实现】
#include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> using namespace std; #define maxn 20001 int fa[maxn],ans[maxn]; int find(int x) { if(fa[x] != x) fa[x] = find(fa[x]);//路径压缩 return fa[x]; } void unionn(int r1,int r2)//合并 { fa[r2] = r1; } /*bool judge(int x,int y) { x = find(x); y = find(y); if(x == y) return true; else return false; }*/ bool cmp(int a,int b) { return a<=b; } int main() { int m,n,i,x,y,r1,r2; int tot = 0; scanf("%d",&n); scanf("%d",&m); for(i = 1;i <= n;i++) fa[i] = i; for(i = 1;i <= m;i++) { scanf("%d%d",&x,&y); r1 = find(x); r2 = find(y); if(r1 != r2)unionn(r1,r2); } for(i = 1;i <= n;i++) { ans[find(i)]++; } /*for(i = 1;i <= n;i++) { printf("%d\n",ans[i]); }*/ (for(int i = 1;i <= n;i++) { if(ans[i] > 0)tot++; } printf("%d",tot); return 0; }