犯罪团伙

【题目描述】

警察抓到了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;
}

 

posted @ 2018-03-22 00:45  我的露娜不会飘  阅读(271)  评论(0编辑  收藏  举报