【并查集】模板 + 【HDU 1213、HDU 1232、POJ 2236、POJ 1703】例题详解
不想看模板,想直接看题目的请戳下面目录:
目录:
POJ 2236 Wireless Network 【传送门】
POJ 1703 Find them, Catch them 【传送门】
先上模板:
- #define MAXN 根据编号需要
- int per[MAXN],rank[MAXN];
- void init(int n)
- {
- int i;
- for(i=;i<=n;i++)
- {
- per[i]=i;rank[i]=
- }
- }
- int find(int x)
- {
- if(x==per[x])
- return x;
- return per[x]=find(per[x]);
- }
- void unite(int x,int y)
- {
- x=find(x);
- y=find(y);
- if(x==y)
- return;
- if(rank[x]>rank[y])
- per[y]=x;
- else
- {
- per[x]=y;
- if(rank[x]==rank[y])
- rank[y]+=;
- }
- }
- bool same(int x,int y)
- {
- return find(x)==find(y);
- }
并查集详解请访问:(通俗易懂解释)
https://blog.csdn.net/lesileqin/article/details/96703143
接下来来看几个例子:
HDU 1213 How Many Tables【传送门】
题目大意:有一个人过生日,请到了他的诸多朋友,但是这些朋友之间有的认识,有的不认识。这个人想尽可能的把相互之间认识的人凑到一张桌子上,不认识的人则去另一张桌子。朋友们互相认识的规则是:比如A认识B,B认识C,那么A,B,C就可以凑到一桌子上。现在问:他的朋友们以这样的规则能凑够几桌。
解题思路:运用并查集,把相互认识的人都联合一下,到最后计算数组中有几个per值等于自身的人就可以了。
- #include<iostream>
- using namespace std;
- int f[];
- void init(int n)
- {
- for(int i=;i<=n;i++)
- f[i]=i;
- }
- int find(int a)
- {
- while(a!=f[a])
- {
- a=f[a];
- }
- return a;
- }
- void Combin(int a,int b)
- {
- int ta,tb;
- ta=find(a);
- tb=find(b);
- if(ta!=tb)
- f[ta]=tb;
- }
- int answer(int n)
- {
- int sum=;
- for(int i=;i<=n;i++)
- if(f[i]==i)
- sum++;
- return sum;
- }
- int main()
- {
- int t;
- cin >> t;
- while(t--)
- {
- int n,m;
- cin >> n >> m;
- init(n);
- int a,b;
- for(int i=;i<m;i++)
- cin >> a >> b,Combin(a,b);
- cout << answer(n) << endl ;
- }
- return ;
- }
HDU 1232 畅通工程 【传送门】
此处略去题目大意……
解题思路:其实这个题目和上一个题异曲同工,就是变换了一下条件,我们假设村子是上题的朋友,那么路就是朋友们之间的关系,那么两堆朋友之间连线,其实只需要一次就可以了,同样,三堆朋友之间要想相互认识,只需要两个关系就可以了,所以我们很容易可以得出朋友们要想都认识,只需要把朋友堆数 - 1 就可以了。
代码只需要将上题的输出结果-1即可 ,不再贴出。
POJ 2236 Wireless Network 【传送门】
题目大意:一片废弃的地方,ACM协会正在进行救援,可是所有电脑的通信设施都被破坏了,被修复好的电脑的信号传输距离只有d米,现在给出所有电脑的坐标,然后给出指令S与O,S后跟两个数字代表两个电脑的编号,询问这两台电脑是否能通信,如果能则输出SUCCESS,否则输出FALL;O后跟一个编号,代表修好一定电脑。
解题思路:此题运用并查集,在修复好一台电脑之后遍历所有修好的电脑,如果两点间距离小于d米,则连通两个点;遇到S的时候只需要判断是否连通即可。
- #include<iostream>
- #include<cmath>
- using namespace std;
- #define MAXN 1005
- //坐标
- struct pos{
- int x,y;
- int par_id; //父辈id
- int rank; //树的高度
- };
- struct pos par[MAXN]; //记录父亲
- bool isok[MAXN]; //是否被修复成功
- float Distance(struct pos a,struct pos b)
- {
- float dis=sqrt(fabs(a.x-b.x)*fabs(a.x-b.x)+fabs(a.y-b.y)*fabs(a.y-b.y));
- return dis;
- }
- void init(int n)
- {
- for(int i=;i<=n;i++)
- {
- par[i].par_id=i;
- par[i].rank=;
- isok[i]=false;
- }
- }
- //查树根
- int find(int x)
- {
- if(par[x].par_id==x)
- return x;
- else
- return par[x].par_id=find(par[x].par_id);
- }
- //合并
- void unite(int x,int y)
- {
- x=find(x);
- y=find(y);
- if(x==y)
- return;
- if(par[x].rank<par[y].rank)
- par[x].par_id=y;
- else{
- par[y].par_id=x;
- if(par[x].rank==par[y].rank)
- par[x].rank++;
- }
- }
- //判断x和y是不是同一个集合
- bool same(int x,int y)
- {
- return find(x)==find(y);
- }
- int main()
- {
- int N,d;
- char c;
- cin >> N >> d;
- init(N);
- for(int i=;i<=N;i++)
- {
- int x,y;
- cin >> x >> y;
- par[i].x=x;
- par[i].y=y;
- }
- while(cin >> c)
- {
- int p,q;
- if(c=='O')
- {
- cin >> p;
- isok[p]=true;
- for(int i=;i<=N;i++)
- {
- if(isok[i]==true&&i!=p)
- {
- struct pos a,b;
- a.x=par[i].x;
- a.y=par[i].y;
- a.par_id=par[i].par_id;
- b.x=par[p].x;
- b.y=par[p].y;
- b.par_id=par[p].par_id;
- if(Distance(a,b)<=d)
- {
- // cout << Distance(a,b) << endl;
- unite(p,i);
- }
- }
- }
- }
- else if(c=='S')
- {
- cin >> p >> q;
- if(same(p,q))
- cout << "SUCCESS\n";
- else
- cout << "FAIL\n";
- }
- // for(int i=1;i<=N;i++)
- // cout << par[i].par_id << " ";
- // cout << endl;
- }
- return ;
- }
POJ 1703 Find them, Catch them 【传送门】
题目大意:有两个帮派,警察现在抓住了N个罪犯,输入字母A或D,后面跟着两个罪犯的编号。如果字符是A,则代表这一次询问:如果不是一个帮派,则输出“In different gangs.”;是一个帮派输出"In the same gang.";否则输出“Not sure yet.”。如果是字符D,那就代表着后面的两个编号不属于一个帮派!
解题思路:需要一个标记数组vis,用来标记两个罪犯(a,b)不属于同一个帮派,如果标记数组两个罪犯都为0则代表不确定是哪个帮派,并且让vis[a]=b,vis[b]=a;如果vis[a]==0&&vis[b]!=0,则让vis[b]=a,并且连通a,vis[b];如果vis[b]==0&&vis[a]!=0,则让vis[a]=b,并且连通b,vis[a];最后一种情况是a与b的标记数组都不为0,那么连2019-07-21通a,vis[b],连通b,vis[a]最后判断即可。
- //#include<iostream>
- //using namespace std;
- #include<stdio.h>
- #define MAXN 100010
- int per[MAXN],rank[MAXN],vis[MAXN];
- void init(int n)
- {
- int i;
- for(i=;i<=n;i++)
- {
- per[i]=i;rank[i]=;vis[i]=;
- }
- }
- int find(int x)
- {
- if(x==per[x])
- return x;
- return per[x]=find(per[x]);
- }
- void unite(int x,int y)
- {
- x=find(x);
- y=find(y);
- if(x==y)
- return;
- if(rank[x]>rank[y])
- per[y]=x;
- else
- {
- per[x]=y;
- if(rank[x]==rank[y])
- rank[y]+=;
- }
- }
- int Same(int x,int y)
- {
- if(find(x)==find(y))
- return ;
- }
- int main()
- {
- // ios::sync_with_stdio(false);
- int t;
- scanf("%d",&t);
- // cin >> t;
- while(t--)
- {
- int n,m;
- scanf("%d%d",&n,&m);
- // cin >> n >> m;
- init(n);
- while(m--)
- {
- int a,b;
- char c[];
- scanf("%s",c);
- //getchar();
- scanf("%d%d",&a,&b);
- //cin >> c >> a >> b;
- //联立
- //printf("%c\n",c);
- if(c[]=='D')
- {
- if(vis[a]==&&vis[b]==)
- {
- vis[a]=b;
- vis[b]=a;
- }
- else if(vis[a]==)
- {
- vis[a]=b;
- unite(a,vis[b]);
- }
- else if(vis[b]==)
- {
- vis[b]=a;
- unite(b,vis[a]);
- }
- else{
- unite(a,vis[b]);
- unite(b,vis[a]);
- }
- }
- else{
- if(Same(a,b))
- printf("In the same gang.\n");
- //cout << "In the same gang.\n";
- else if(Same(a,vis[b]))
- //cout << "In different gangs.\n";
- printf("In different gangs.\n");
- else
- printf("Not sure yet.\n");
- //cout << "Not sure yet.\n";
- }
- }
- }
- return ;
- }
【并查集】模板 + 【HDU 1213、HDU 1232、POJ 2236、POJ 1703】例题详解的更多相关文章
- HDU 1213 How Many Tables(并查集模板)
http://acm.hdu.edu.cn/showproblem.php?pid=1213 题意: 这个问题的一个重要规则是,如果我告诉你A知道B,B知道C,这意味着A,B,C知道对方,所以他们可以 ...
- 【2018寒假集训Day 8】【并查集】并查集模板
Luogu并查集模板题 #include<cstdio> using namespace std; int z,x,y,n,m,father[10001]; int getfather(i ...
- POJ-图论-并查集模板
POJ-图论-并查集模板 1.init:把每一个元素初始化为一个集合,初始化后每一个元素的父亲节点是它本身,每一个元素的祖先节点也是它本身(也可以根据情况而变). void init() { for ...
- 【并查集模板】 【洛谷P2978】 【USACO10JAN】下午茶时间
P2978 [USACO10JAN]下午茶时间Tea Time 题目描述 N (1 <= N <= 1000) cows, conveniently numbered 1..N all a ...
- 【并查集模板】并查集模板 luogu-3367
题目描述 简单的并查集模板 输入描述 第一行包含两个整数N.M,表示共有N个元素和M个操作. 接下来M行,每行包含三个整数Zi.Xi.Yi 当Zi=1时,将Xi与Yi所在的集合合并 当Zi=2时,输出 ...
- Memcached集群/分布式/高可用 及 Magent缓存代理搭建过程 详解
当网站访问量达到一定时,如何做Memcached集群,又如何高可用,是接下来要讨论的问题. 有这么一段文字来描述“Memcached集群” Memcached如何处理容错的? 不处理!:) 在memc ...
- Android查缺补漏(IPC篇)-- 款进程通讯之AIDL详解
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8436529.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...
- Android查缺补漏(IPC篇)-- 进程间通讯之AIDL详解
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8436529.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...
- HDU 1213 - How Many Tables - [并查集模板题]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1213 Today is Ignatius' birthday. He invites a lot of ...
随机推荐
- Delphi中取得汉字的首字母(十分巧妙)
function Tdm.GetHzPy(const AHzStr: string): string;const ChinaCode: array[0..25, 0..1] of Integer = ...
- FMX中实现PostMessage的方法
首先,做为一个从Windows下转过来的开发人员,许多时候,我们喜欢用PostMessage来触发一些异步执行的代码,但遗憾的是,FMX做为EMB的跨平台库的基础,只提供了SendMessage方法, ...
- PHP中关于时间、时区、本地时间、UTC时间、GMT时间、时间戳等知识的梳理
在PHP开发中,我们经常会在时间问题上被搞糊涂,比如我们希望显示一个北京时间,但是当我们使用date函数进行输出时,却发现少了8个小时.几乎所有的php猿类都必须对php中几个重要的时间转换等方法进行 ...
- Mac上使用brew安装nvm来支持多版本的Nodejs
brew方式 如果机器没有安装过node,那么首先brew install nvm安装nvm. 其次需要在shell的配置文件(~/.bashrc, ~/.profile, or ~/.zshrc)中 ...
- Netty源码分析--创建Channel(三)
恩~,没错,其实这一篇才是真正的开始分析源码,你打我呀~. 先看一下我Netty的启动类 private void start() throws Exception { EventLoopGroup ...
- Django高级编程之自定义Field实现多语言
自定义数据库字段 扩展默认的models.CharField和models.TextField使之成为支持多语言的字段. 可以轻松实现复用,无需配置多余选项 from django.conf impo ...
- 使用wireshark捕获SSL/TLS包并分析
原创博客,转载请注出处! TLS运作方式如下图:
- Scala 学习之路(六)—— 常用集合类型之 List & Set
一.List字面量 List是Scala中非常重要的一个数据结构,其与Array(数组)非常类似,但是List是不可变的,和Java中的List一样,其底层实现是链表. scala> val l ...
- 【Linux杂记】Linux配置静态IP地址,修改主机名、host
博主使用的系统是:乌班图16.04 1.设置静态IP方法如下: #sudo vim /etc/network/interfaces #修改如下部分: auto eth0//ipconfig命令查看网卡 ...
- .NET Core IdentityServer4实战 第六章-Consent授权页
在identityServer4中登陆页面只要是成功了,就会注册一个Cookie在服务器资源上,像现在大部分的网站第三方授权,都是经过一个页面,然后选需要的功能,IdentityServer4也给我们 ...