码迷,mamicode.com
首页 > 其他好文 > 详细

【海岛帝国模拟赛】No.2 海岛帝国:“落汤鸡”市的黑帮危机

时间:2016-06-21 20:28:16      阅读:154      评论:0      收藏:0      [点我收藏+]

标签:

50200210海岛帝国:“落汤鸡”市的黑帮危机

【试题描述】

        近几天,犯罪分子发现“药师傅”帝国的警力约等于0。(请见YSF的海岛帝国)于是开始猖狂了起来。他们选择了依山靠水(农村?)的“落汤鸡”市。开始抢劫财务,一天内发生了5起抢劫案,9起爆炸案,3起枪击案,12起绑架案!搞得YSF夜不能寐,况且每天还有那么多“怪物”要处理。于是,所有的责任都落在了“落汤鸡”市可怜的市长LTJ上。犯罪分子在LTJ市有了好多好多个窝点。市民开始惊慌起来,背井离乡。“郭同学”TONY由于YSF没还债而拒绝伸出援手。这时,YSF的“购物券”WHT提出了一个建议:请大名鼎鼎的“李易峰”探长来化解危机。于是,没主见的YSF照办了。不过,由于犯罪团伙庞大,作案频繁,数量众多,LTJ调查不清楚有几个犯罪团伙。不过,LYF探长还是搜集到了一些线索。助理YSM提出:强盗的同伙的同伙也是同伙,只有没有关系的才各自为政。请你帮帮可怜的LTJ、YSF、LYF,编一个程序来告诉他们有几个独立的犯罪团伙。


【输入要求】

        * 第一行n表示强盗的人数,m表示警方搜集到的m条线索
        * 接下来m行,每行两个整数a,b表示强盗a,b是同伙 

 

【输出要求】

         * 一个数:表示有几个独立的犯罪团伙

 

【输入实例】

10 9
1 2
3 4
5 2
4 6
2 6
8 7
9 7
1 6
2 4

【输出实例】

3

 

【其他说明】

【试题分析】

         一看到什么团伙,什么信息这样的词,就一定知道要用神奇的“并查集”了。那我们先来看看:什么是并查集吧!

 

 

1. 简述

 

    并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。常常在使用中以森林来表示。
    需要实现的操作有:合并两个集合,判断两个元素是否属于一个集合。
    这里介绍的主要是普通的并查集,很多情况下使用的并查集是需要扩展的,根据使用情况的不同,有很多差别,这里仅仅是最基本的算法。

 

经典的find函数:

int findset(int x)    
{    
    if(pa[x] != x)    
    {           
        int root = findset(pa[x]);     
        return pa[x] = root;    
    }    
    else    
    {    
        return x;    
    }    
}    

 

初始化:

void init()     //初始化   该函数可以根据具体情况保存和初始化需要的内容    
{    
    for(int i = 0; i < MAX_SIZE; i++)    
    {    
        pa[i] = i;    
    }   
}    
  

1、Make_Set(x) 把每一个元素初始化为一个集合

初始化后每一个元素的父亲节点是它本身,每一个元素的祖先节点也是它本身(也可以根据情况而变)。

2、Find_Set(x) 查找一个元素所在的集合

查找一个元素所在的集合,其精髓是找到这个元素所在集合的祖先!这个才是并查集判断和合并的最终依据。
判断两个元素是否属于同一集合,只要看他们所在集合的祖先是否相同即可。
合并两个集合,也是使一个集合的祖先成为另一个集合的祖先,具体见示意图

3、Union(x,y) 合并x,y所在的两个集合

合并两个不相交集合操作很简单:
利用Find_Set找到其中两个集合的祖先,将一个集合的祖先指向另一个集合的祖先。如图

 

并查集的优化

1、Find_Set(x)时 路径压缩
寻找祖先时我们一般采用递归查找,但是当元素很多亦或是整棵树变为一条链时,每次Find_Set(x)都是O(n)的复杂度,有没有办法减小这个复杂度呢?
答案是肯定的,这就是路径压缩,即当我们经过"递推"找到祖先节点后,"回溯"的时候顺便将它的子孙节点都直接指向祖先,这样以后再次Find_Set(x)时复杂度就变成O(1)了,如下图所示;可见,路径压缩方便了以后的查找。

 

2、Union(x,y)时 按秩合并
即合并的时候将元素少的集合合并到元素多的集合中,这样合并之后树的高度会相对较小。

 1技术分享int father[MAX];   /* father[x]表示x的父节点*/
 2技术分享int rank[MAX];     /* rank[x]表示x的秩*/
 3技术分享
 4技术分享
 5技术分享/* 初始化集合*/
 6技术分享void Make_Set(int x)
 7技术分享{
 8技术分享    father[x] = x; //根据实际情况指定的父节点可变化
 9技术分享    rank[x] = 0;   //根据实际情况初始化秩也有所变化
10技术分享}
11技术分享
12技术分享
13技术分享/* 查找x元素所在的集合,回溯时压缩路径*/
14技术分享int Find_Set(int x)
15技术分享{
16技术分享    if (x != father[x])
17技术分享    {
18技术分享        father[x] = Find_Set(father[x]); //这个回溯时的压缩路径是精华
19技术分享    }
20技术分享    return father[x];
21技术分享}
22技术分享
23技术分享
24技术分享/* 
25技术分享   按秩合并x,y所在的集合
26技术分享   下面的那个if else结构不是绝对的,具体根据情况变化
27技术分享   但是,宗旨是不变的即,按秩合并,实时更新秩。
28技术分享*/
29技术分享void Union(int x, int y)
30技术分享{
31技术分享    x = Find_Set(x);
32技术分享    y = Find_Set(y);
33技术分享    if (x == y) return;
34技术分享    if (rank[x] > rank[y]) 
35技术分享    {
36技术分享        father[y] = x;
37技术分享    }
38技术分享    else
39技术分享    {
40技术分享        if (rank[x] == rank[y])
41技术分享        {
42技术分享            rank[y]++;
43技术分享        }
44技术分享        father[x] = y;
45技术分享    }
46技术分享}
47技术分享

并且传说中的并查集按秩合并:

 1 const int MAXSIZE = 500;
 2 int uset[MAXSIZE];
 3 int rank[MAXSIZE];
 4  
 5 void makeSet(int size) {
 6     for(int i = 0;i < size;i++)  uset[i] = i;
 7     for(int i = 0;i < size;i++)  rank[i] = 0;
 8 }
 9 int find(int x) {
10     if (x != uset[x]) uset[x] = find(uset[x]);
11     return uset[x];
12 }
13 void unionSet(int x, int y) {
14     if ((x = find(x)) == (y = find(y))) return;
15     if (rank[x] > rank[y]) uset[y] = x;
16     else {
17         uset[x] = y;
18         if (rank[x] == rank[y]) rank[y]++;
19     }
20 }

这题只是并查集的基础,用到了find和merge合并

剩下的就貌似只有sum++了

【代码】

 

技术分享
 1 #include<iostream>
 2 using namespace std;
 3 int f[50]={0},n,m,k,sum=0;
 4 void init()
 5 {
 6      int i;
 7      for(i=1;i<=n;i++) f[i]=i;
 8      return ;
 9 }
10 int getf(int v)
11 {
12     if(f[v]==v) return v;
13     else
14     {
15         f[v]=getf(f[v]);
16         return f[v];
17     }
18 }
19 void merge(int v,int u)
20 {
21      int t1,t2;
22      t1=getf(v);
23      t2=getf(u);
24      if(t1!=t2) f[t2]=t1;
25      return ;
26 }
27 int main()
28 {
29     int i,x,y;
30     scanf("%d%d",&n,&m);
31     init();
32     for(i=1;i<=m;i++)
33     {
34         scanf("%d%d",&x,&y);
35         merge(x,y);
36     } 
37     for(i=1;i<=n;i++)
38         if(f[i]==i) sum++;
39     printf("%d\n",sum);
40 }
View Code

 

【海岛帝国模拟赛】No.2 海岛帝国:“落汤鸡”市的黑帮危机

标签:

原文地址:http://www.cnblogs.com/wxjor/p/5604647.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!