基础训练(一):并查集练习汇总

关于并查集的基础知识,这里不再赘述,这里写的很详细并查集基础这里只想汇总一下最近的练习

食物链(poj:1182)

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

思路:

经典!!并查集经典基础题目。重点是找出题目中三种动物的相互关系,然后根据矢量计算推导出关系式。

AC代码:

#include
#include

using namespace std;
const int maxn = 50010;
int n, m;
int fa[maxn], team[maxn];

int find(int x)
{
	if (fa[x] == x)
		return fa[x];
	int tmp = fa[x];
	fa[x] = find(tmp);
	team[x] = (team[x] + team[tmp]) % 3;//根据父亲结点推导出子结点的关系
	return fa[x];
}

void union_set(int op, int x, int y)
{
	int fx = find(x);
	int fy = find(y);
	if (fx != fy)
	{
		fa[fy] = fx;//让y的祖先成为x的祖先的儿子,也就是把两个集合合并,
		team[fy] = (team[x] + 4 - op - team[y] + 3) % 3;
		//根据两个子节点的关系推导出y的祖先的关系
	}
}

int main()
{
	int D, x, y;
	int ans = 0;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i)fa[i] = i;//初始化
	while (m--)
	{
		scanf("%d%d%d", &D, &x, &y);
		if (x > n || y > n)
		{
			ans++;
			continue;
		}
		if (D == 2 && x == y)
		{
			ans++;
			continue;
		}
		int rx = find(x);
		int ry = find(y);
		if (rx != ry)
			union_set(D, x, y);
		else if ((team[x] - team[y] + 3) % 3 != D - 1)
			ans++;
	}
	printf("%d\n", ans);
	return 0;
}

Find them, Catch them(poj:1703)

Time Limit: 1000MS Memory Limit: 10000K

Description

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.

Input

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.

Output

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.”

Sample Input

1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4

Sample Output

Not sure yet.
In different gangs.
In the same gang.

思路:

思路类似于前面的食物链的题目思路,只不过关系比前面的食物链要简单

AC代码:

#include
#include

using namespace std;
const int maxn = 100010;
int n, m;
int fa[maxn], team[maxn];

int find(int x)
{
	if (fa[x] == x)
		return fa[x];
	int tmp = fa[x];
	fa[x] = find(tmp);
	team[x] = (team[x] + team[tmp]) % 2;
	return fa[x];
}

void union_set(int x, int y)
{
	int fx = find(x);
	int fy = find(y);
	if (fx != fy)
	{
		fa[fy] = fx;
		team[fy] = (team[x] - team[y] + 1) % 2;
	}
}

int main()
{
	int T, i, x, y;
	char ch;
	scanf("%d", &T);
	while (T--)
	{
		memset(team, 0, sizeof team);
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; ++i)fa[i] = i;
		while (m--)
		{
			ch = getchar();
			while(ch!='\n')
				ch = getchar();
			ch = getchar();
			scanf("%d%d", &x, &y);
			if (ch == 'D')
				union_set(x, y);
			else
			{
				int rx = find(x);
				int ry = find(y);
				if (rx != ry)
					printf("Not sure yet.\n");
				else if (team[x] != team[y])
					printf("In different gangs.\n");
				else if (team[x] == team[y])
					printf("In the same gang.\n");
			}
		}
	}
	return 0;
}

Cube Stacking(poj:1988)

Time Limit: 2000MS Memory Limit: 30000K

Description

Farmer John and Betsy are playing a game with N (1 <= N <= 30,000)identical cubes labeled 1 through N. They start with N stacks, each containing a single cube. Farmer John asks Betsy to perform P (1<= P <= 100,000) operation. There are two types of operations:
moves and counts.

  • In a move operation, Farmer John asks Bessie to move the stack containing cube X on top of the stack containing cube Y.
  • In a count operation, Farmer John asks Bessie to count the number of cubes on the stack with cube X that are under the cube X and report that value.

Write a program that can verify the results of the game.

Input

  • Line 1: A single integer, P

  • Lines 2…P+1: Each of these lines describes a legal operation. Line 2 describes the first operation, etc. Each line begins with a ‘M’ for a move operation or a ‘C’ for a count operation. For move operations, the line also contains two integers: X and Y.For count operations, the line also contains a single integer: X.

Note that the value for N does not appear in the input file. No move operation will request a move a stack onto itself.

Output

Print the output from each of the count operations in the same order as the input file.

Sample Input

6
M 1 6
C 1
M 2 4
M 2 6
C 3
C 4

Sample Output

1
0
2

思路:

加权并查集

代码:

#include

using namespace std;
const int maxn = 30010;
int num[maxn], ans[maxn], fa[maxn];

int find(int x)
{
	if (fa[x] == x)
		return fa[x];
	int tmp = fa[x];
	fa[x] = find(fa[x]);
	ans[x] += ans[tmp];
	return fa[x];
}

void union_set(int x, int y)
{
	int fx = find(x);
	int fy = find(y);
	if (fx != fy)
	{
		fa[fx] = fy;
		ans[fx] = num[fy];
		num[fy] += num[fx];
	}
}

int main()
{
	int T, x, y, z;
	char ch;
	for (int i = 1; i < maxn; ++i)
	{
		fa[i] = i;
		num[i] = 1;
	}
	scanf("%d", &T);
	while (T--)
	{
		ch = getchar();
		while (ch != '\n')
			ch = getchar();
		ch = getchar();
		if (ch == 'M')
		{
			scanf("%d%d", &x, &y);
			union_set(x, y);
		}
		else
		{
			scanf("%d", &z);
			find(z);
			printf("%d\n", ans[z]);
		}
	}
	return 0;
}

This Sentence is False

Time Limit: 1000MS Memory Limit: 65536K

Description

The court of King Xeon 2.4 is plagued with intrigue and conspiracy. A document recently discovered by the King’s Secret Service is thought to be part of some mischievous scheme. The document contains simply a set of sentences which state the truth or falsehood of each other. Sentences have the form “Sentence X is true/false” where X identifies one sentence in the set. The King’s Secret Service suspects the sentences in fact refer to another, yet uncovered, document.
While they try to establish the origin and purpose of the document, the King ordered you to find whether the set of sentences it contains is consistent, that is, if there is a valid truth assignment for the sentences. If the set is consistent, the King wants you to determine the maximum number of sentences which can be made true in a valid truth assignment for the document.

Input

The input contains several instances of documents. Each document starts with a line containing a single integer,
N, which indicates the number of sentences in the document (1 <= N <= 1000). The following N lines contain each a sentence. Sentences are numbered sequentially, in the order they appear in the input (the first is sentence 1, the second is sentence 2, and so on). Each sentence has the form “Sentence X is true.” or “Sentence X is false.”, where 1 <= X <= N. The value N = 0 indicates the end of input.

Output

For each document in the input your program should output one line. If the document is consistent,your program should print the maximum number of sentences in a valid truth assignment for the document.Otherwise your program should print the word ‘Inconsistent’.

Sample Input

1
Sentence 1 is false.
1
Sentence 1 is true.
5
Sentence 2 is false.
Sentence 1 is false.
Sentence 3 is true.
Sentence 3 is true.
Sentence 4 is false.
0

Sample Output

Inconsistent
1
3

思路:

这个题有难度,题目大意是要求判断给定的一组句子样例是否是一致的(consistent),如果一致就找出能够使得这组文本样例正确的最大句子总数,否则就输出Inconsistent。仔细研究会发现悖论形成的条件,也就是句子A和句子B互相评价对方且评价相反的时候会相悖。如果出现这种矛盾的结果,那么一定是Inconsistent。所以我们需要把互相描述的两个句子合并到一个集合中去,然后根据集合中子节点对根节点的描述来判断是否相悖。如果不相悖再对每一个集合中子节点对根节点的描述进行统计,取最大值即可

代码:

#include
#include
#include

using namespace std;
const int maxn = 1010;
char str[10];
int fa[maxn], t[maxn], f[maxn];
bool status[maxn];

int find(int x)
{
	if (fa[x] == x)
		return fa[x];
	int tmp = find(fa[x]);
	status[x] = status[x] ? status[fa[x]] : !status[fa[x]];
	fa[x] = tmp;
	return fa[x];
}

bool union_set(int x, int y, bool flg)
{
	int fx = find(x);
	int fy = find(y);
	if (fx == fy)
	{
		if (status[x] == status[y])
			return flg;
		else
			return !flg;
	}
	if (status[x] == status[y])
		status[fx] = flg;
	else
		status[fx] = !flg;
	fa[fx] = fy;
	return true;
}

int main()
{
	int n;
	int i, y, root, ans = 0;
	bool flag;
	while (scanf("%d", &n), n != 0)
	{
		for (i = 0; i <= n; ++i) {
			fa[i] = i; t[i] = 0;
			status[i] = 1; f[i] = 0;
		}
		flag = true;
		for (i = 1; i <= n; ++i)
		{
			getchar();
			scanf("Sentence %d is %s.", &y, str);
			if (flag && str[0] == 't')
				flag = union_set(i, y, 1);
			if (flag && str[0] == 'f')
				flag = union_set(i, y, 0);
		}
		if (!flag)
			printf("Inconsistent\n");
		else
		{
			ans = 0;
			for (i = 1; i <= n; ++i) {
				root = find(i);
				if (status[i])
					t[root]++;
				else
					f[root]++;
			}
			for (i = 1; i <= n; ++i)
			{
				ans += max(t[i], f[i]);
			}
			printf("%d\n", ans);
		}
	}
	return 0;
}

你可能感兴趣的:(并查集)