主题:[原创]并查集模板
Sedgewick
[专家分:0] 发布于 2008-03-11 20:33:00
最近经常遇到需要用Union-Find Data-Structure来解决的题目,于是根据资料实现了一个。发现蛮好用的,希望对你能有帮助。
# include <iostream>
# include <vector>
# include <cassert>
using namespace std;
class UnionFindSets
{
public:
// makeSet(x): create a set that contains the single element x.
[b]void makeSet(int l, int r);[/b]
// find(x): return the set that contains x.
[b]int find(int x);[/b]
// unite(A,B) - return the set which is the union of A and B. Namely A ∪ B. Namely,
// this operation merges the two sets A and B and return the merged set.
[b]void unite(int x, int y);[/b]
bool connected(int x, int y);
int differentSet(int l, int r);
private:
vector<int> parent;
vector<int> rank;
};
void UnionFindSets::makeSet(int l, int r)
{
// The initial partition has each element in a set by itself.
assert(1 <= l && l <= r);
[b]parent.resize(r + 1);[/b]
[b]rank.resize(r + 1);[/b]
for (int x = l; x <= r; ++x) {
parent[x] = x;
rank[x] = 0;
}
}
int UnionFindSets::find(int x)
{
if (x != parent[x]) {
// Since, anyway, we travel the path to the root during a find operation,
// we might as well hung all the nodes on the path directly on the root.
[b]parent[x] = find(parent[x]);[/b]
}
return parent[x];
}
void UnionFindSets::unite(int x, int y)
{
int a = find(x);
int b = find(y);
if (rank[a] > rank[b]) {
// Always hang the smaller tree on the larger tree.
parent[b] = a;
} else if (rank[a] < rank[b]) {
parent[a] = b;
} else {
parent[a > b ? a : b] = a < b ? a : b;
++rank[a > b ? a : b];
}
}
bool UnionFindSets::connected(int x, int y)
{
return find(x) == find(y);
}
int UnionFindSets::differentSet(int l, int r)
{
int count = 0;
for (int x = l; x <= r; ++x) {
if (parent[x] == x) {
++count;
}
}
return count;
}
[url=http://blog.csdn.net/Sedgewick]感兴趣的来我的窝看看。[/url]
最后更新于:2009-01-21 11:11:00
回复列表 (共7个回复)
沙发
Sedgewick [专家分:0] 发布于 2008-03-11 20:35:00
直接运用并查集的题目。
pku acm 2524 Ubiquitous Religions
Time Limit: 5000MS Memory Limit: 65536K
[b]Description[/b]
There are so many different religions in the world today that it is difficult to keep track of them all. You are interested in finding out how many different religions students in your university believe in.
You know that there are n students in your university (0 < n <= 50000). It is infeasible for you to ask every student their religious beliefs. Furthermore, many students are not comfortable expressing their beliefs. One way to avoid these problems is to ask m (0 <= m <= n(n-1)/2) pairs of students and ask them whether they believe in the same religion (e.g. they may know if they both attend the same church). From this data, you may not know what each person believes in, but you can get an idea of the upper bound of how many different religions can be possibly represented on campus. You may assume that each student subscribes to at most one religion.
[b]Input[/b]
The input consists of a number of cases. Each case starts with a line specifying the integers n and m. The next m lines each consists of two integers i and j, specifying that students i and j believe in the same religion. The students are numbered 1 to n. The end of input is specified by a line in which n = m = 0.
[b]Output[/b]
For each test case, print on a single line the case number (starting with 1) followed by the maximum number of different religions that the students in the university believe in.
[b]Sample Input[/b]
10 9
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
10 4
2 3
4 5
4 8
5 8
0 0
[b]Sample Output[/b]
Case 1: 1
Case 2: 7
[b]Hint[/b]
Huge input, scanf is recommended.
(提交的时候还担心因为用了STL而超时呢)
int main()
{
[b]UnionFindSets ufs;[/b]
int n, m, cases = 0;
while (cin >> n >> m) {
if (n == 0 && m == 0) {
break;
}
++cases;
[b]ufs.makeSet(1, n);[/b]
int k;
for (k = 0; k < m; ++k) {
int i, j;
cin >> i >> j;
[b]ufs.unite(i, j);[/b]
}
cout << "Case " << cases << ": " << [b]ufs.differentSet(1, n)[/b] << endl;
}
return 0;
}
板凳
Sedgewick [专家分:0] 发布于 2008-03-11 23:03:00
pku acm 2236 Wireless Network
Time Limit:10000MS Memory Limit:65536K
[b]Description [/b]
An earthquake takes place in Southeast Asia. The ACM (Asia Cooperated Medical team) have set up a wireless network with the lap computers, but an unexpected aftershock attacked, all computers in the network were all broken. The computers are repaired one by one, and the network gradually began to work again. Because of the hardware restricts, each computer can only directly communicate with the computers that are not farther than d meters from it. But every computer can be regarded as the intermediary of the communication between two other computers, that is to say computer A and computer B can communicate if computer A and computer B can communicate directly or there is a computer C that can communicate with both A and B.
In the process of repairing the network, workers can take two kinds of operations at every moment, repairing a computer, or testing if two computers can communicate. Your job is to answer all the testing operations.
[b]Input [/b]
The first line contains two integers N and d (1 <= N <= 1001, 0 <= d <= 20000). Here N is the number of computers, which are numbered from 1 to N, and D is the maximum distance two computers can communicate directly. In the next N lines, each contains two integers xi, yi (0 <= xi, yi <= 10000), which is the coordinate of N computers. From the (N+1)-th line to the end of input, there are operations, which are carried out one by one. Each line contains an operation in one of following two formats:
1. "O p" (1 <= p <= N), which means repairing computer p.
2. "S p q" (1 <= p, q <= N), which means testing whether computer p and q can communicate.
The input will not exceed 300000 lines.
[b]Output [/b]
For each Testing operation, print "SUCCESS" if the two computers can communicate, or "FAIL" if not.
[b]Sample Input [/b]
4 1
0 1
0 2
0 3
0 4
O 1
O 2
O 4
S 1 4
O 3
S 1 4
[b]Sample Output [/b]
FAIL
SUCCESS
struct point {
int _x;
int _y;
bool _worked;
point(int x, int y, bool w) : _x(x), _y(y) , _worked(w) {}
};
bool cover(point src, point dst, int r)
{
return ((src._x - dst._x) * (src._x - dst._x) + (src._y - dst._y) * (src._y - dst._y)) <= r * r;
}
void repair(UnionFindSets &ufs, vector<point> &pos, int p, int d)
{
pos[p]._worked = true;
for (int i = 1; i < pos.size(); ++i) {
if (pos[i]._worked && cover(pos[i], pos[p], d)) {
[b]ufs.unite(i, p);[/b]
}
}
}
int main()
{
[b]UnionFindSets ufs;[/b]
int n, d;
cin >> n >> d;
[b]ufs.makeSet(1, n);[/b]
vector<point> computers;
computers.push_back(point(-1, -1, false));
for (int i = 1; i <= n; ++i) {
int xi, yi;
cin >> xi >> yi;
computers.push_back(point(xi, yi, false));
}
char operation;
while (cin >> operation) {
if (operation == 'O') {
// "O p" (1 <= p <= N), which means repairing computer p.
int p;
cin >> p;
repair(ufs, computers, p, d);
} else {
// "S p q" (1 <= p, q <= N),
// which means testing whether computer p and q can communicate.
int p, q;
cin >> p >> q;
if ([b]ufs.connected(p, q)[/b]) {
cout << "SUCCESS" << endl;
} else {
cout << "FAIL" << endl;
}
}
}
return 0;
}
3 楼
ghbxx2004 [专家分:610] 发布于 2008-03-11 23:18:00
写这个会有多少人看啊,你的PKU ID是多少啊?
4 楼
Sedgewick [专家分:0] 发布于 2008-03-11 23:24:00
我很菜的,只是不满意那些只满足于AC而不注重程序的大牛才写这些的。
我叫Pope。
5 楼
neulinux [专家分:420] 发布于 2008-03-13 01:57:00
你写模板就行了,还给标程,xiedi要是知道了会警告你的!!呵呵呵
6 楼
Sedgewick [专家分:0] 发布于 2008-03-15 14:34:00
pku acm 2492 A Bug's Life
Time Limit: 10000MS Memory Limit: 65536K
[size=1][b]Description[/b][/size]
[b]Background [/b]
Professor Hopper is researching the sexual behavior of a rare species of bugs. He assumes that they feature two different genders and that they only interact with bugs of the opposite gender. In his experiment, individual bugs and their interactions were easy to identify, because numbers were printed on their backs.
[b]Problem [/b]
Given a list of bug interactions, decide whether the experiment supports his assumption of two genders with no homosexual bugs or if it contains some bug interactions that falsify it.
[b]Input[/b]
The first line of the input contains the number of scenarios. Each scenario starts with one line giving the number of bugs (at least one, and up to 2000) and the number of interactions (up to 1000000) separated by a single space. In the following lines, each interaction is given in the form of two distinct bug numbers separated by a single space. Bugs are numbered consecutively starting from one.
[b]Output[/b]
The output for every scenario is a line containing "Scenario #i:", where i is the number of the scenario starting at 1, followed by one line saying either "No suspicious bugs found!" if the experiment is consistent with his assumption about the bugs' sexual behavior, or "Suspicious bugs found!" if Professor Hopper's assumption is definitely wrong.
[b]Sample Input[/b]
2
3 3
1 2
2 3
1 3
4 2
1 2
3 4
[b]Sample Output[/b]
Scenario #1:
Suspicious bugs found!
Scenario #2:
No suspicious bugs found!
[b]Hint[/b]
在我认识的人中,我的朋友的朋友是我的朋友;我的敌人的敌人是我的朋友。
郁闷了好久的一道题。我一直把它们分成male和female两类,陷入了泥潭。其实应该站在bug的角度分成同性和异性。即维护两个并查集fellow和enemy。如果i和j有往来,就说明i和j的异性是同类;同样,j和i的异性是同类。
if there is an interaction between bug i and bug j, then
fellow.unite(i, enemy[j])
fellow.unite(j, enemy[i])
enemy[i] = fellow.find(j)
enemy[j] = fellow.find(i)
这样原来的模板就需要更改了。
class UnionFindSets
{
public:
……
void makeSet(int l, int r, int [b]initValue[/b]);
……
[b]public:[/b]
vector<int> parent;
vector<int> rank;
};
void UnionFindSets::makeSet(int l, int r, int [b]initValue[/b])
{
……
for (int x = l; x <= r; ++x) {
parent[x] = [b]initValue[/b];
rank[x] = 0;
}
}
[b]const int null = -1;[/b]
int UnionFindSets::find(int x)
{
[b]if (x < 0) {
return null;
}[/b]
……
}
void UnionFindSets::unite(int x, int y)
{
[b]if (x < 0 || y < 0) {
return;
}[/b]
……
}
struct couple {
int _x;
int _y;
couple(int x, int y) : _x(x), _y(y) {}
};
bool findHomosexualBugs(vector<couple> &A, UnionFindSets &fellows, UnionFindSets &enemy);
int main()
{
[b] UnionFindSets fellows, enemy;[/b]
int scenarioe;
cin >> scenarioe;
for (int i = 1; i <= scenarioe; ++i) {
int numOfBugs, numOfInteractions;
cin >> numOfBugs >> numOfInteractions;
vector<couple> interactions;
interactions.reserve(numOfBugs + 1);
[b]fellows.makeSet(1, numOfBugs);
enemy.makeSet(1, numOfBugs, null);[/b]
for (int j = 0; j < numOfInteractions; ++j) {
int n, m;
cin >> n >> m;
interactions.push_back(couple(n, m));
}
cout << "Scenario #" << i << ":" << endl;
if (findHomosexualBugs(interactions, fellows, enemy)) {
cout << "Suspicious bugs found!" << endl;
} else {
cout << "No suspicious bugs found!" << endl;
}
cout << endl;
}
return 0;
}
bool findHomosexualBugs(vector<couple> &A, UnionFindSets &fellows, UnionFindSets &enemy)
{
for (int i = 0; i < A.size(); ++i) {
int f = fellows.find(A[i]._x), m = fellows.find(A[i]._y);
if (fellows.connected(f, m)) {
return true;
} else {
[b]// my fellow's fellow is my fellow;
// my enemy's enemy is my fellow.[/b]
fellows.unite(enemy.parent[f], m);
fellows.unite(enemy.parent[m], f);
enemy.parent[fellows.find(f)] = fellows.find(m);
enemy.parent[fellows.find(m)] = fellows.find(f);
}
}
return false;
}
7 楼
Sedgewick [专家分:0] 发布于 2008-03-15 16:16:00
pku acm 1703 Find them, Catch them
Time Limit:1000MS Memory Limit:10000K
[b]
Description [/b]
The police office in Tadu City decides to say ends to the chaos, as launch actions to root up the TWO gangs in the city, Gang Dragon and Gang Snake. However, the police first needs to identify which gang a criminal belongs to. The present question is, given two criminals; do they belong to a same clan? You must give your judgment based on incomplete information. (Since the gangsters are always acting secretly.)
Assume N (N <= 10^5) criminals are currently in Tadu City, numbered from 1 to N. And of course, at least one of them belongs to Gang Dragon, and the same for Gang Snake. You will be given M (M <= 10^5) messages in sequence, which are in the following two kinds:
1. D [a] [b]
where [a] and [b] are the numbers of two criminals, and they belong to different gangs.
2. A [a] [b]
where [a] and [b] are the numbers of two criminals. This requires you to decide whether a and b belong to a same gang.
[b]Input [/b]
The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases. Then T cases follow. Each test case begins with a line with two integers N and M, followed by M lines each containing one message as described above.
[b]Output [/b]
For each message "A [a] [b]" in each case, your program should give the judgment based on the information got before. The answers might be one of "In the same gang.", "In different gangs." and "Not sure yet."
[b]Sample Input [/b]
1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4
[b]Sample Output [/b]
Not sure yet.
In different gangs.
In the same gang.
和A Bug's Life 一模一样,这时我才理解enemy和fellow不同。enemy只指示其对手所在的集合(最小的元素),而不是其父节点。
const string answers[3] = { "In the same gang.", "In different gangs.", "Not sure yet."};
int gangsters(UnionFindSets &fellows, UnionFindSets &enemy, int a, int b)
{
if (fellows.connected(a, b)) {
return 0;
} else if (enemy.parent[a] != null &&
enemy.parent[b] != null &&
enemy.parent[a] == b &&
enemy.parent[b] == a) {
return 1;
} else {
return 2;
}
}
int main()
{
[b]UnionFindSets fellows, enemy;[/b]
int t;
cin >> t;
for (int i = 1; i <= t; ++i) {
int n, m;
cin >> n >> m;
[b]fellows.makeSet(1, n);
enemy.makeSet(1, n, null);[/b]
for (int j = 1; j <= m; ++j) {
char message;
int a, b;
cin >> message >> a >> b;
if (message == 'D') {
// criminal a and b belong to different gangs.
// my fellow's fellow is my fellow;
// my enemy's enemy is my fellow.
fellows.unite(enemy.parent[a], b);
fellows.unite(enemy.parent[b], a);
enemy.parent[fellows.find(a)] = fellows.find(b);
enemy.parent[fellows.find(b)] = fellows.find(a);
} else {
cout << answers[gangsters(fellows, enemy, a, b)] << endl;
}
}
}
return 0;
}
我来回复