《挑战程序设计竞赛》2.4.2 数据结构-并查集 POJ1182 2236 1703 AOJ2170
来源:互联网 发布:cgi编程 编辑:程序博客网 时间:2024/06/09 20:46
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 CodeProblem: 1182 User: liangrx06Memory: 1368K Time: 266MSLanguage: C++ Result: AcceptedSource 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 CodeProblem: 2236 User: liangrx06Memory: 296K Time: 1500MSLanguage: C++ Result: AcceptedSource 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 CodeProblem: 1703 User: liangrx06Memory: 1772K Time: 344MSLanguage: C++ Result: AcceptedSource 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.2 数据结构-并查集 POJ1182 2236 1703 AOJ2170
- POJ 1182-食物链 [并查集] 《挑战程序设计竞赛》2.4
- POJ 2236 Wireless Network [并查集] 《挑战程序设计竞赛》2.4
- POJ1182 数据结构 (并查集)
- 《挑战程序设计竞赛》2.4 加工并存储数据的数据结构
- 《挑战程序设计竞赛》P87 题目:Find them, Catch them Poj 1703 并查集
- aoj2170(并查集操作更改)
- AOJ2170 Marked Ancestor 并查集
- 并查集 poj1182
- POJ1182-并查集
- poj1182----并查集
- 并查集--poj1182
- 并查集&poj1182
- poj1182(并查集)
- 挑战程序竞赛系列(10):2.4并查集
- 挑战程序设计竞赛 POJ 1192食物链 带权并查集,略麻烦
- poj 1703 poj1182(并查集)
- 食物链 POJ1182 -- 并查集
- Lintcode - Naive Fibonacci
- OSGI系列 Web-Service
- git fetch.git merge,git pull
- 浅谈尾递归的优化方式
- 102. Binary Tree Level Order Traversal
- 《挑战程序设计竞赛》2.4.2 数据结构-并查集 POJ1182 2236 1703 AOJ2170
- 不遇到问题才不正常
- 【个人笔记】CLR Via C# 第一章 CLR执行模型
- 黑马程序员——面向对象_抽象类与接口的区别
- BZOJ1492: [NOI2007]货币兑换Cash
- 浅谈线程池(上):线程池的作用及CLR线程池
- 【ENVI错误】 OBJ_NEW:Error encoutered during the sparse operation
- 如何在ListView中点击item控制item中的控件变化(ListView篇)
- 浅谈线程池(中):独立线程池的作用及IO线程池