《挑战程序设计竞赛》2.4 数据结构-并查集 POJ1182 2236 1703 AOJ2170
POJ1182
http://poj.org/problem?id=1182
题目
难得的中文题。。。
食物链
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 56252 Accepted: 16485
Description
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是”1 X Y”,表示X和Y是同类。
第二种说法是”2 X Y”,表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。
Input
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
Output
只有一个整数,表示假话的数目。
Sample Input
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
Sample Output
3
思路
对于每只动物i创建3个元素i-A,i-B,i-C,并用3×N个元素建立并查集,维护如下信息:
- i-X表示i属于X
- 如果i-A和j-B在同一个组里,那么如果i属于A,j就一定属于B。如果j属于B,i就一定属于A。
我们对每条信息进行如下操作:
- 如果x或者y比N大或者小于1,则答案+1
- x和y属于同一种类。如果已知x和y不属于同一类,则答案+1。否则合并x-A和y-A、x-B和y-B、x-C和y-C
- x吃y。如果已知x和y属于同一类或者x-A和y-C在同一个集合,则答案+1。否则合并x-A和y-B、x-B和y-C、x-C和y-A
最后,输出答案。
需要注意的是一定要有路径压缩,即使用rank平衡各树的长度,否则很有可能超时。
还有一种解法是带权并查集,参见http://blog.csdn.net/balloons2012/article/details/7871104
代码
Source Code
Problem: 1182 User: liangrx06
Memory: 1368K Time: 266MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 50000;
int n;
int pre[3*N+1];
int rank[3*N+1];
void init()
{
for (int i = 1; i <= 3*n; i ++) {
pre[i] = i;
rank[i] = 0;
}
}
int find(int a)
{
while (a != pre[a])
a = pre[a];
return a;
}
void unite(int a, int b)
{
a = find(a);
b = find(b);
if (a == b) return;
if (rank[a] < rank[b]) {
pre[a] = b;
} else {
pre[b] = a;
if (rank[a] == rank[b])
rank[a] ++;
}
}
bool same(int a, int b)
{
return find(a) == find(b);
}
int main(void)
{
int k;
int d, a, b;
int res = 0;
cin >> n >> k;
init();
while ( k-- ) {
scanf("%d%d%d", &d, &a, &b);
if ( a > n || b > n ) {
res ++;
}
else if ( d == 1 ) {
if ( same(a, b+n) || same(a, b+2*n) ) {
res ++;
} else {
unite(a, b);
unite(a+n, b+n);
unite(a+2*n, b+2*n);
}
}
else {
if ( same(a, b) || same(a, b+2*n) ) {
res ++;
} else {
unite(a, b+n);
unite(a+n, b+2*n);
unite(a+2*n, b);
}
}
}
printf("%d\n", res);
return 0;
}
POJ2236
http://poj.org/problem?id=2236
题意
一张图上分布着n台坏了的电脑,并知道它们的坐标。两台修好的电脑如果距离<=d就可以联网,也可以通过其他修好的电脑间接相连。给出操作“O x”表示修好x,给出操作“S x y”,请你判断x和y在此时有没有连接上。
思路
基本并查集题目,每次O x都对所有与x相连的电脑做更新,加入并查集,S x y时直接判断是否相连即可。
我的代码是书中所给并查集的标准实现。
代码
Source Code
Problem: 2236 User: liangrx06
Memory: 296K Time: 1500MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <set>
#include <string>
#include <cmath>
using namespace std;
const int N = 1001;
struct Node {
int x, y;
};
int n, d;
Node node[N+1];
int pre[N+1];
int rank[N+1];
void init()
{
for (int i = 1; i <= n; i ++) {
pre[i] = i;
rank[i] = 0;
}
}
int find(int a)
{
while (a != pre[a])
a = pre[a];
return a;
}
void unite(int a, int b)
{
a = find(a);
b = find(b);
if (a == b) return;
if (rank[a] < rank[b]) {
pre[a] = b;
} else {
pre[b] = a;
if (rank[a] == rank[b])
rank[a] ++;
}
}
bool same(int a, int b)
{
return find(a) == find(b);
}
void input()
{
cin >> n >> d;
for (int i = 1; i <= n; i ++) {
scanf("%d%d", &node[i].x, &node[i].y);
}
}
double dis(int a, int b)
{
int x2 = (node[a].x-node[b].x) * (node[a].x - node[b].x);
int y2 = (node[a].y-node[b].y) * (node[a].y - node[b].y);
return sqrt((double)(x2 + y2));
}
void solve()
{
string s;
int a, b;
set<int> rep;
while ( cin >> s ) {
if (s == "O") {
scanf("%d", &a);
if ( rep.find(a) == rep.end() ) {
set<int>::iterator it;
for (it = rep.begin(); it != rep.end(); it ++) {
if ( dis(a, *it) <= d ) {
//printf("unite %d %d\n", a, *it);
unite(a, *it);
}
}
rep.insert(a);
}
} else {
scanf("%d%d", &a, &b);
//printf("%d %d %d %d\n", a, b, find(a), find(b));
if ( same(a, b) )
printf("SUCCESS\n");
else
printf("FAIL\n");
}
}
}
int main(void)
{
input();
init();
solve();
return 0;
}
POJ1703
http://poj.org/problem?id=1703
题意
在这个城市里有两个黑帮团伙,现在给出N个人,问任意两个人他们是否在同一个团伙。
输入D x y代表x于y不在一个团伙里。
输入A x y要输出x与y是否在同一团伙或者不确定他们在同一个团伙里。
思路
这个题是poj1182的简单版,具体不再赘述。
代码
Source Code
Problem: 1703 User: liangrx06
Memory: 1772K Time: 344MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 100000;
int n;
int pre[2*N+1];
int rank[2*N+1];
void init()
{
for (int i = 1; i <= 2*n; i ++) {
pre[i] = i;
rank[i] = 0;
}
}
int find(int a)
{
while (a != pre[a])
a = pre[a];
return a;
}
void unite(int a, int b)
{
a = find(a);
b = find(b);
if (a == b) return;
if (rank[a] < rank[b]) {
pre[a] = b;
} else {
pre[b] = a;
if (rank[a] == rank[b])
rank[a] ++;
}
}
bool same(int a, int b)
{
return find(a) == find(b);
}
int main(void)
{
int t, m;
char s[2];
int a, b;
cin >> t;
while ( t-- ) {
cin >> n >> m;
init();
while ( m-- ) {
scanf("%s%d%d", s, &a, &b);;
if ( s[0] == 'D' ) {
unite(a, b+n);
unite(a+n, b);
} else {
if ( same(a, b) )
printf("In the same gang.\n");
else if ( same(a, b+n) )
printf("In different gangs.\n");
else
printf("Not sure yet.\n");
}
}
}
return 0;
}
AOJ2170
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2170
题意
给定一棵树,对于这棵树有两个操作,M i表示将i的所有子孙节点以i为树根从原树中分离出来,Q i表示询问节点i的树根是多少。
思路
用数组pre[i]记录节点i的父亲节点,若i是根节点就记为i,这个题中1是根节点。
这样对于两种操作我们只要如下处理即可:
- M i:将tree[i]改为它本身,也就是i
- Q i:从i向上查找直到找到它的根节点
代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 100000;
int n, p;
int pre[N+1];
int find(int x)
{
while (x != pre[x])
x = pre[x];
return x;
}
int main(void)
{
while (scanf("%d%d", &n, &p) != EOF) {
if (!n && !p) break;
pre[1] = 1;
for (int i = 2; i <= n; i ++)
scanf("%d", &pre[i]);
long long sum = 0;
for (int i = 1; i <= p; i ++) {
char s[2];
int x;
scanf("%s%d", s, &x);
if (s[0] == 'M')
pre[x] = x;
else
sum += find(x);
}
printf("%lld\n", sum);
}
return 0;
}
《挑战程序设计竞赛》2.4 数据结构-并查集 POJ1182 2236 1703 AOJ2170的更多相关文章
- HDU 5923 Prediction(2016 CCPC东北地区大学生程序设计竞赛 Problem B,并查集)
题目链接 2016 CCPC东北地区大学生程序设计竞赛 B题 题意 给定一个无向图和一棵树,树上的每个结点对应无向图中的一条边,现在给出$q$个询问, 每次选定树中的一个点集,然后真正被选上的是这 ...
- POJ 2386 Lake Counting 题解《挑战程序设计竞赛》
地址 http://poj.org/problem?id=2386 <挑战程序设计竞赛>习题 题目描述Description Due to recent rains, water has ...
- Aizu 2249Road Construction 单源最短路变形《挑战程序设计竞赛》模板题
King Mercer is the king of ACM kingdom. There are one capital and some cities in his kingdom. Amazin ...
- 《挑战程序设计竞赛》2.3 动态规划-优化递推 POJ1742 3046 3181
POJ1742 http://poj.org/problem?id=1742 题意 有n种面额的硬币,面额个数分别为Ai.Ci,求最多能搭配出几种不超过m的金额? 思路 据说这是传说中的男人8题呢,对 ...
- 挑战程序设计竞赛》P345 观看计划
<挑战程序设计竞赛>P345 观看计划 题意:一周一共有M个单位的时间.一共有N部动画在每周si时 ...
- 算法手记 之 数据结构(并查集详解)(POJ1703)
<ACM/ICPC算法训练教程>读书笔记-这一次补上并查集的部分.将对并查集的思想进行详细阐述,并附上本人AC掉POJ1703的Code. 在一些有N个元素的集合应用问题中,通常会将每个元 ...
- ACM数据结构-并查集
ACM数据结构-并查集 并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合 ...
- 种类并查集——带权并查集——POJ1182;HDU3038
POJ1182 HDU3038 这两个题比较像(一类题目),属于带权(种类)并查集 poj1182描绘得三种动物种类的关系,按照他一开始给你的关系,优化你的种类关系网络,最后看看再优化的过程中有几处矛 ...
- poj 1182 食物链 并查集 题解《挑战程序设计竞赛》
地址 http://poj.org/problem?id=1182 题解 可以考虑使用并查集解决 但是并不是简单的记录是否同一组的这般使用 每个动物都有三个并查集 自己 天敌 捕食 并查集 那么在获得 ...
随机推荐
- UIPopoverController具体解释
今天一位童鞋问我个问题.大意是popoverController不会显示.经过我寻找问题发现以下这种方法不好掌控. 为什么说他不好掌控那.我这个给大家带来一个列子.通过这个列子来介绍PopoverCo ...
- osgi应用使用桥接的方式打成war包部署在websphere上时遇到的与cxf相关的问题
原来我们的程序都是基于Equinox架构的,可是后面由于要实现打成war包在中间件中部署的需求,使用了eclipse官方提供的桥接方式实现. 桥接的部分后面有时间了我专门写一个文章来说,不明确的临时请 ...
- Solr3.6.2和Solr4.9.0经常使用配置
tomcat 以tomcat 7为例,位置/work/apache-tomcat-7.0.55 Solr 3.6.2 基本配置 Solr 3.6.2.须要JDK 6/JDK7支持. 下载Solr 3. ...
- 实战Nginx负载均衡高冗余高可用WEB架构
最近公司主力网站之一改版完成终于上线了,牵扯了我大半年的时间,现在终于有时间坐下来写点东西,总结沉淀一下自己的技术心得.此次,根据服务器的数量和质量,我采用负载均衡高冗余的架构,考虑单点故障,W ...
- phpcms v9实现调用多个栏目id的方法
{pc:content action="position" posid="14" catid="13,14,15,16,17,18,19,20,21& ...
- Decoration1:Spring-boot基础实现
前段时间发布的Traveller项目,花费了不少精力,但是效果并不如意,根源在于瀑布式的开发思想不适合这种独立的学习项目.在项目初始就规划一个全面的web系统,,因为预设了一个前景,在心理上会想尽快看 ...
- shell script 在if 的判断条件正则表达式=~中引号问题
今天在脚本里运行if判断的时候,总是进不了对应的分支,检查正则表达式也没有错误.单独拿到shell里面执行还是显示没有匹配.比较奇怪,就搜了下,才发现是在=~ 后面的正则表达式上不能加上引号,而且以点 ...
- java模拟http请求上传文件,基于Apache的httpclient
1.依赖 模拟http端的请求需要依赖Apache的httpclient,需要第三方JSON支持,项目中添加 <dependency> <groupId>org.apache& ...
- windows静态库的使用
最近在学Zeromq(像框架一样的一个socket library)的使用,里面有关于库的使用问题.今天就来复习下静态库的使用: 使用静态库是重用代码的一种绝佳方式.您不必在自己创建的每个程序中重新实 ...
- 在同一台电脑上添加多个ssh key
1.创建新的ssh key: ssh-keygen -t rsa -C "your_email@email.com" 然后让你输入新的文件名称,这里设置为new # 设置名称为En ...